[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into stage-aosp-master am: 3959c12574 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/19835916

Change-Id: Iece72c58cd831a967465b835bd724400076aaf66
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Cronet/Android.bp b/Cronet/Android.bp
new file mode 100644
index 0000000..3ce88ef
--- /dev/null
+++ b/Cronet/Android.bp
@@ -0,0 +1,106 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_import {
+    name: "cronet_impl_native_java",
+    jars: ["prebuilt/cronet_impl_native_java.jar"],
+    visibility: ["//visibility:private"],
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "29",
+}
+
+java_import {
+    name: "cronet_impl_common_java",
+    jars: ["prebuilt/cronet_impl_common_java.jar"],
+    visibility: ["//visibility:private"],
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "29",
+}
+
+java_import {
+    name: "cronet_impl_platform_java",
+    jars: ["prebuilt/cronet_impl_platform_java.jar"],
+    visibility: ["//visibility:private"],
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "29",
+}
+
+cc_prebuilt_library_shared {
+    name: "libcronet.107.0.5284.2",
+    shared_libs: [
+        "libandroid",
+        "libc",
+        "libdl",
+        "liblog",
+        "libm",
+    ],
+    stl: "libc++_static",
+    target: {
+        android_arm64: {
+            srcs: ["prebuilt/libs/arm64-v8a/libcronet.107.0.5284.2.so"],
+        },
+        android_arm: {
+            srcs: ["prebuilt/libs/armeabi-v7a/libcronet.107.0.5284.2.so"],
+        },
+        android_x86_64: {
+            srcs: ["prebuilt/libs/x86_64/libcronet.107.0.5284.2.so"],
+        },
+        android_x86: {
+            srcs: ["prebuilt/libs/x86/libcronet.107.0.5284.2.so"],
+        },
+    },
+    // These are already stripped, and restripping them just issues diagnostics.
+    strip: {
+        none: true,
+    },
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "29",
+}
+
+genrule {
+    name: "cronet_api-src",
+    srcs: ["prebuilt/cronet_api-src.jar"],
+    cmd: "cp $(in) $(out)",
+    out: [
+        "cronet_api-src.srcjar",
+    ],
+}
+
+java_sdk_library {
+    name: "framework-cronet",
+    defaults: ["framework-module-defaults"],
+    srcs: [
+        ":cronet_api-src",
+    ],
+    libs: [
+        "androidx.annotation_annotation",
+    ],
+    static_libs: [
+        "androidx.core_core-nodeps",
+        "cronet_impl_common_java",
+        "cronet_impl_native_java",
+        "cronet_impl_platform_java",
+    ],
+    apex_available: ["com.android.tethering"],
+    jarjar_rules: "jarjar-rules.txt",
+    unsafe_ignore_missing_latest_api: true,
+    dist_group: "android",
+    // cronet is used as a shared library.
+    shared_library: true,
+    exclude_kotlinc_generated_files: true,
+}
diff --git a/Cronet/AndroidManifest.xml b/Cronet/AndroidManifest.xml
new file mode 100644
index 0000000..f3b3c3e
--- /dev/null
+++ b/Cronet/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.android.cronet"
+  android:versionCode="11"
+  android:versionName="R-initial">
+</manifest>
diff --git a/Cronet/OWNERS b/Cronet/OWNERS
new file mode 100644
index 0000000..62c5737
--- /dev/null
+++ b/Cronet/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
diff --git a/Cronet/TEST_MAPPING b/Cronet/TEST_MAPPING
new file mode 100644
index 0000000..815d496
--- /dev/null
+++ b/Cronet/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "CronetApiTest"
+    }
+  ]
+}
diff --git a/Cronet/apex/Android.bp b/Cronet/apex/Android.bp
new file mode 100644
index 0000000..29155cc
--- /dev/null
+++ b/Cronet/apex/Android.bp
@@ -0,0 +1,59 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// CronetApexDefaults uses apex_defaults cronet_apex_defaults specifies. cronet_apex_defaults
+// could be "CronetApexDefaultsEnabled" or "CronetApexDefaultsDisabled" depending on the branch.
+cronet_apex_defaults = "CronetApexDefaultsDisabled"
+// This is a placeholder comment to avoid merge conflicts
+// as cronet_apex_defaults may have different values
+// depending on the branch
+
+apex_defaults {
+    name: "CronetApexDefaults",
+    defaults: [cronet_apex_defaults],
+}
+
+apex_defaults {
+    name: "CronetApexDefaultsEnabled",
+    jni_libs: ["libcronet.107.0.5284.2"],
+    java_libs: ["framework-cronet"],
+    arch: {
+        riscv64: {
+            // TODO: remove this when there is a riscv64 libcronet
+            exclude_jni_libs: ["libcronet.107.0.5284.2"],
+        },
+    },
+}
+
+apex_defaults {
+    name: "CronetApexDefaultsDisabled",
+}
+
+// TODO: Remove cronet apex after com.android.cronet is removed from PRODUCT_PACKAGES
+apex {
+    name: "com.android.cronet",
+    key: "com.android.cronet.key",
+    updatable: false,
+    manifest: "manifest.json",
+}
+
+apex_key {
+    name: "com.android.cronet.key",
+    public_key: "com.android.cronet.avbpubkey",
+    private_key: "com.android.cronet.pem",
+}
diff --git a/Cronet/apex/AndroidManifest.xml b/Cronet/apex/AndroidManifest.xml
new file mode 100644
index 0000000..650badc
--- /dev/null
+++ b/Cronet/apex/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.android.cronet">
+  <!-- APEX does not have classes.dex -->
+  <application android:hasCode="false" />
+  <uses-sdk
+      android:minSdkVersion="29"
+  />
+</manifest>
diff --git a/Cronet/apex/com.android.cronet.avbpubkey b/Cronet/apex/com.android.cronet.avbpubkey
new file mode 100644
index 0000000..38aebe0
--- /dev/null
+++ b/Cronet/apex/com.android.cronet.avbpubkey
Binary files differ
diff --git a/Cronet/apex/com.android.cronet.pem b/Cronet/apex/com.android.cronet.pem
new file mode 100644
index 0000000..438653f
--- /dev/null
+++ b/Cronet/apex/com.android.cronet.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAqpxkMMK57w+fzxLcwF+mEQEbKrDFWXYHL697tv8DBu/aL2tM
+LRHiKFdov0Fsnup++bd9oojI+6qyAyJh4I8nvzKc4onM0eXL++0FPZuiTv6a6r7K
+wyn+NVT2X/0yr2Hs5NL1rKXFmJPAfMoRCW9vQdi5xMlM9QN8mNaqSWddKtrQM3yC
+HDLy2zZd7MlQ1UTnmDqGCG0AVtuM34X0v1o0wAL7UwNdzOARtmnuWzcL5vBwAJg8
+eXFZH5Pt5rITxbU+eYw/V+/sUYyI0Anrj4GG+oyWQxgdxz+FwdpSAt14xtw5IDl7
+pTYqEIm+TQKnWe6NzVWfI89s2nh85hgOSgugfpS4symbfbZd4qNSOcHGQTB1ssvf
+vdYGF1WRFA20VEEOMzTMYsvcfModKNVCpibzHu/SYDIJGL0JyJWu2eIE+mrn3lLD
+nwDZ2P39YyVNpritEkbk5qugaLh9mIFqAD4L4niYLu/AtYq+CXzd7yroM2ycleRq
+JRNsCIUx3A/Z5vKx7IJaMb7Pwap3DAe3u3/2L9NCL6oo074Rf7fh5xkDF4Dua3Gg
+kPw+k183jAQECHORstdVlgVZlMxPif4lxQ8uCHJWxyDsgShPgzMtEjJyrJJQyDon
+X8pN4xqb+WX30XNYBK5sp7x6mc8w4rdAGExhUSdmTS0J/lcfar6A6/j/yg8CAwEA
+AQKCAgAiYafTJ7q+kWB8I2n3Ho9hx95IqRzsHVvvYSbGRve+MyG+App0TrFLvemu
++SlBkTILcs3Prk8KYGjFNu2QimjRIAr7oBd1iSClYSt4Md/wmWBwxAgqclD3QGry
+Bx1quIo7xsOZikKar9PPkg0C4MED/P/ax1JJ4ez/A+uHJVxiIXxpk8LImf/U60zc
+RemTQPKG++w80HKMDmyCMwWSdkRBGZi6Luh9O/51yz0shphQbs2zYPp24r+6HF6J
+6gMQCalQZ1Hwj7oI6RA9FHKzFcA0x5YUaUy+9W8oFK4IQ8duE70zYEIplhO+B3Qh
+ItLEzc0nvwR1+/wMvtE0sU5X36X06AFhkcpjyAdmNVMiG91KGrZqruOufrOQu4VE
+njJ5VEUvWOr+6inZDWIdT3NQvwJCZkT4Vvn9WBAoKM8pkpfhY5HTGq4ttX7McHjF
+p7YBFbHRpzO+OfSwM7f/xq2WLcjOKgsFgv17CUo7KQo1rqWPD/4IKJKW5n6dzDwX
+RRF4dehUMYK2UNAbcN9S8O8nJGcJfb76FExjZtGLrhlDgawLmu71bbq5afot/IQ9
+JuB4KxG59t2JAHGNgM7WfFvcL1Zp1D+kzDEHYKYkyDJh3qB7Tw+StlAQFSQjNBX/
+c1I6DUl68rSHOc2LF4oK8ID1oe/URMg2YoaLlF8un2nGZBfwGQKCAQEA20Ie5hBC
+H8W1vTtyI4jOdn/h11RL8UuLN0xtXYunYCzD4+gAEKsAShbhKnvINThZaoxQ2sxy
+9EB+2Ob8R9muYAxR4Eu4tDBWedYmJEl74MflLMFnIrtFIKy4F54zk7J6uNG3cHRo
+yTpoIcmK101xzz1Ed9Dd0XL6rpegnWIuVlWV4slGAf1l3h6pycfx+HCYcCboz6Nq
+JMA0ioKY0fgz5q+mb7IIObN8JjGdLeBQtNh3fcXby08AT/03psKhjgBsbuyTs7s7
+E3n+jxJ3p4xHP/psU3D/HgKWewp48AJNHY8MLY+Kp0KNoQMSQKHKqsiluGTCKLTJ
+sCWg/2c8xf728wKCAQEAxzNdZ54e6WXexMMIsGoH+wVknTHiizuxN4ZqzY/nCM0M
+9heooChfMAFrq8XMC/6MXgiy5rBhl3H12HlG4S6cq3J07MsMR0N+sUFEobuZURB4
++CbEBebmXx1kymC6FrRG4JuKZWbQ8AKpfd28QO7x915safeq6FA8cILJn4oqBGqQ
+Y8TzMKtuaCzQGoBjSgiMpx1fo9Stl7+dpGiPNsnQEG+SEsXxKoZweJPsuK8G+ZgB
+8YARajLwFfgfsCNp/8fXjA98BYM8eSxURa7USUyCmY4hU3WIGG5qc0/C69Fl35ex
+YZG7XQo1DxW/+gWsdUbAFbI4y+iJ8SRpsFxtFYifdQKCAQA5DiW4PHbYibxXN8bl
+1E3VrEV6oSb57WyWwT6cXyD49+0pu095BuaWYQnK4lcg8j7iaQ0JQraPNNFNZB42
+HEEyIUKVGV9BFGsMXVujibPAtIPAd7t84DqG3Csziillv8YLnhccHk6+PoKmeCm3
+CSIaiZjtjN6MCF2PXUmgatIgCTltwG6FSgleGaCZL3yZ58LjPFzM23tdgN6rRHy7
+9tiaqQ6odi2JxlkCH1sFex/FT6cYhYpCh5ZPOldm/7LGnvmYi9uLo6cl1FMXq/iT
+Ev/feC0EMZ1Rk97QudLqsc6baIQEvxuXlswAICp5wyBX/MqTBzU3HoR1X/VbQOQh
+qc1dAoIBACLjNhqtsNBDzS480krDZz5phWOalwi3naQR4Ka760S5VOnM3vWd3H31
+4bul2sTHAiJ994c7oPv7M4mERAuwNDQ6yYunTDE2+vtkaPbCemmeLvGXKIG4HOTP
+qxVet3i+fiNcWnLD/Rfr/29R5GSi9LHUUbyFaeNiGhPCdDmC4zT+zOcMWWNOwvlv
+z8q0ba9LrAaguF1jJDwNjTh8L4jy84PNZpHvJPvDq/MSRUVbMieInd6EBYjJ/w55
+9GLO8QOhJnkbRSdaAr9eKixCIF/uDHmEUQXi8cEFpZMohwTyGZt9X82szlnPLdfE
+gWjykW/AwmeKXTQpN++J5xDCP0CkOvkCggEBAND4wviHue/+bqY9R5EAIDIZn6kx
+VCK9roebvGjq/Az5AM7IiyUQRANv+6CmeIlXCRBMbaAtgQxhvW5UodA1livgVuzR
+ElF7Br3wfikZ34oWY0Qu8fZI0ru4syAoTiGpaUPJJgStYi76dqne15usTRk/MvwP
+tJzqpkWUYcmNv3g/w92Wr1nIJYXlPKrANSSppRg0P/CAPOHkPxLF0RwC5yIZzC5C
+hiTXSE9AwCFllMRKInnbhUdy/L+xUL6mAbGVvD/DHE7Q2xmPSsdEX5nTZkhEnW/L
+TLLgbsy2+8ouvMJCSNaMq77jq7iCIMTogEfA5GX0UO2Kjf4pnjREOzNQg4I=
+-----END RSA PRIVATE KEY-----
diff --git a/Cronet/apex/com.android.cronet.pk8 b/Cronet/apex/com.android.cronet.pk8
new file mode 100644
index 0000000..a63d761
--- /dev/null
+++ b/Cronet/apex/com.android.cronet.pk8
Binary files differ
diff --git a/Cronet/apex/com.android.cronet.x509.pem b/Cronet/apex/com.android.cronet.x509.pem
new file mode 100644
index 0000000..c9cd874
--- /dev/null
+++ b/Cronet/apex/com.android.cronet.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGIzCCBAugAwIBAgIUKEbkVLro9rIJE3M71D8sgUIngUIwDQYJKoZIhvcNAQEL
+BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRswGQYDVQQDDBJjb20uYW5kcm9pZC5jcm9uZXQxIjAgBgkqhkiG9w0BCQEW
+E2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjA1MDcwOTIxWhgPNDc1NzEwMzEw
+NzA5MjFaMIGfMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEbMBkGA1UEAwwSY29tLmFuZHJvaWQuY3JvbmV0MSIwIAYJKoZIhvcN
+AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAwNXIHR2BeZl/R7tOXgC3KK71rrtK98zucfrNBNp/rUiFqiN527P4
+HLW+CAwnFJGLLzHhSbXiZLXyLS7FfPuMeiULUsv1mIVEVwVhtPvbodxwh5szxlVe
+iVf1WkIV90n2tZojTnhMsiEPt0EBWriPJstdO264snqg+oY9Dktxk4tXtTah/rUI
+KWPHeB0SWQMXZTKb30CVVHZfpG/HCXjUlVLx+14/X56EGi8fxd13q5c56qAybx73
+i6+Sm3+F/jcivDpATuPluvcPaZ4Tel9Nz5NjqjycyXBXh8f8azpN1GkJkltE116W
+ZT1iVIJoDAd540UXhW+TpjTeaEH5OeDOWorQM6nE1N9FMiyReprU7tz+HVmyD44Z
+ah43whi3gmwyjgzscW2Z0xGpgVoHgfz/RyBg5+w6p5AGPR0sewv6zr9HvQhpJsoj
+pl+HD9xcrdGb2yMiwm8RuH9dGW0Nos6p1LiXsxg6VBic3N7S+MGXo/dXdX39tkad
+tD+lNskJUJqyx70ynQtRI66YicyxsFv0BImBC+eGECT3hCgyyxnlvTAeRsVsEWtV
+Z+Xwf5M53/8uXkqGal7cC1bHEkG+0zsR8cc0U+22Nif9HQNIrsjEnybOlP6erV/M
+8yX9umVwDz5QlVKCYJFQOq+awSqR9KD9VX3xIRu8kB6aPtB4A7q3828CAwEAAaNT
+MFEwHQYDVR0OBBYEFBQBcdf/bfLa3knW1ukCOFOt++FtMB8GA1UdIwQYMBaAFBQB
+cdf/bfLa3knW1ukCOFOt++FtMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggIBALG0r81xb6NwaGCF79SeDecbnDE1MbQ2+cRt+eU/Wl2SI2oQqbaa/WP7
+k3vsZvsBFI6r+ZBiKizpbT/N1Fx8pcoH7rsL0MoZRXaCZY6kQJLZN6AAvXEo4NR0
+p01zDvGibVk3z87R98IJHlDPU3NrljxxMCsaUxFEAjae4eS4j8G2TFm6DsTVTeqy
+zveh4NdZFsZSyUN8blChFC/7TytlizeSGNlXLMx8e/VAcxIw+wQPWLk5pNnk3TXu
+HcqwAryW/lt46+iVeTpF4ylFqYyab6Vmf9Vp6+HVatg7YkErJACeoeN4eRv6DfeY
+slMqa2i+W+veVAGP4VApg7iZE5RnNPPHn/80yATKAe0/AztoZpQWZ5g1IAnpCISg
+k65FHVhbkcKdjNQePOGwjictq21KaYFrbXfIwqpejxSqQdLMne02Dj/2kv0dU1jV
+JG9YxVWIpobauYD5mmZvN6PTYJWGQRAsIFFyWfhvnEiohGmVLNEQ3uRaue0yNMXc
+V2t8B81/jNGXwi06qxmjCMnMhucSCSFl2a2V5oiDEcVj4YbaJ+CEJPdopMhQwqab
+EgTkJBeEXoPFenToSIprL/YiuBNvvPETyTYN+hAXh3P7MSeWewkgGhpZhuyzT8F0
+vEo+nt5WhRWl0pEVLFLiOOXR+/LAB3aqrPEdd0NXvxiCb/QaZnCp
+-----END CERTIFICATE-----
diff --git a/Cronet/apex/manifest.json b/Cronet/apex/manifest.json
new file mode 100644
index 0000000..0301e9f
--- /dev/null
+++ b/Cronet/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.cronet",
+  "version": 1
+}
diff --git a/Cronet/api/current.txt b/Cronet/api/current.txt
new file mode 100644
index 0000000..21779bc
--- /dev/null
+++ b/Cronet/api/current.txt
@@ -0,0 +1,175 @@
+// Signature format: 2.0
+package org.chromium.net {
+
+  public abstract class CallbackException extends org.chromium.net.CronetException {
+    ctor protected CallbackException(String, Throwable);
+  }
+
+  public abstract class CronetEngine {
+    ctor public CronetEngine();
+    method public abstract java.net.URLStreamHandlerFactory createURLStreamHandlerFactory();
+    method public abstract byte[] getGlobalMetricsDeltas();
+    method public abstract String getVersionString();
+    method public abstract org.chromium.net.UrlRequest.Builder newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor);
+    method public abstract java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method public abstract void shutdown();
+    method public abstract void startNetLogToFile(String, boolean);
+    method public abstract void stopNetLog();
+  }
+
+  public static class CronetEngine.Builder {
+    ctor public CronetEngine.Builder(android.content.Context);
+    method public org.chromium.net.CronetEngine.Builder addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date);
+    method public org.chromium.net.CronetEngine.Builder addQuicHint(String, int, int);
+    method public org.chromium.net.CronetEngine build();
+    method public org.chromium.net.CronetEngine.Builder enableBrotli(boolean);
+    method public org.chromium.net.CronetEngine.Builder enableHttp2(boolean);
+    method public org.chromium.net.CronetEngine.Builder enableHttpCache(int, long);
+    method public org.chromium.net.CronetEngine.Builder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
+    method public org.chromium.net.CronetEngine.Builder enableQuic(boolean);
+    method public String getDefaultUserAgent();
+    method public org.chromium.net.CronetEngine.Builder setLibraryLoader(org.chromium.net.CronetEngine.Builder.LibraryLoader);
+    method public org.chromium.net.CronetEngine.Builder setStoragePath(String);
+    method public org.chromium.net.CronetEngine.Builder setUserAgent(String);
+    field public static final int HTTP_CACHE_DISABLED = 0; // 0x0
+    field public static final int HTTP_CACHE_DISK = 3; // 0x3
+    field public static final int HTTP_CACHE_DISK_NO_HTTP = 2; // 0x2
+    field public static final int HTTP_CACHE_IN_MEMORY = 1; // 0x1
+  }
+
+  public abstract static class CronetEngine.Builder.LibraryLoader {
+    ctor public CronetEngine.Builder.LibraryLoader();
+    method public abstract void loadLibrary(String);
+  }
+
+  public abstract class CronetException extends java.io.IOException {
+    ctor protected CronetException(String, Throwable);
+  }
+
+  public final class InlineExecutionProhibitedException extends java.util.concurrent.RejectedExecutionException {
+    ctor public InlineExecutionProhibitedException();
+  }
+
+  public abstract class NetworkException extends org.chromium.net.CronetException {
+    ctor protected NetworkException(String, Throwable);
+    method public abstract int getCronetInternalErrorCode();
+    method public abstract int getErrorCode();
+    method public abstract boolean immediatelyRetryable();
+    field public static final int ERROR_ADDRESS_UNREACHABLE = 9; // 0x9
+    field public static final int ERROR_CONNECTION_CLOSED = 5; // 0x5
+    field public static final int ERROR_CONNECTION_REFUSED = 7; // 0x7
+    field public static final int ERROR_CONNECTION_RESET = 8; // 0x8
+    field public static final int ERROR_CONNECTION_TIMED_OUT = 6; // 0x6
+    field public static final int ERROR_HOSTNAME_NOT_RESOLVED = 1; // 0x1
+    field public static final int ERROR_INTERNET_DISCONNECTED = 2; // 0x2
+    field public static final int ERROR_NETWORK_CHANGED = 3; // 0x3
+    field public static final int ERROR_OTHER = 11; // 0xb
+    field public static final int ERROR_QUIC_PROTOCOL_FAILED = 10; // 0xa
+    field public static final int ERROR_TIMED_OUT = 4; // 0x4
+  }
+
+  public abstract class QuicException extends org.chromium.net.NetworkException {
+    ctor protected QuicException(String, Throwable);
+    method public abstract int getQuicDetailedErrorCode();
+  }
+
+  public abstract class UploadDataProvider implements java.io.Closeable {
+    ctor public UploadDataProvider();
+    method public void close() throws java.io.IOException;
+    method public abstract long getLength() throws java.io.IOException;
+    method public abstract void read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer) throws java.io.IOException;
+    method public abstract void rewind(org.chromium.net.UploadDataSink) throws java.io.IOException;
+  }
+
+  public final class UploadDataProviders {
+    method public static org.chromium.net.UploadDataProvider create(java.io.File);
+    method public static org.chromium.net.UploadDataProvider create(android.os.ParcelFileDescriptor);
+    method public static org.chromium.net.UploadDataProvider create(java.nio.ByteBuffer);
+    method public static org.chromium.net.UploadDataProvider create(byte[], int, int);
+    method public static org.chromium.net.UploadDataProvider create(byte[]);
+  }
+
+  public abstract class UploadDataSink {
+    ctor public UploadDataSink();
+    method public abstract void onReadError(Exception);
+    method public abstract void onReadSucceeded(boolean);
+    method public abstract void onRewindError(Exception);
+    method public abstract void onRewindSucceeded();
+  }
+
+  public abstract class UrlRequest {
+    ctor public UrlRequest();
+    method public abstract void cancel();
+    method public abstract void followRedirect();
+    method public abstract void getStatus(org.chromium.net.UrlRequest.StatusListener);
+    method public abstract boolean isDone();
+    method public abstract void read(java.nio.ByteBuffer);
+    method public abstract void start();
+  }
+
+  public abstract static class UrlRequest.Builder {
+    ctor public UrlRequest.Builder();
+    method public abstract org.chromium.net.UrlRequest.Builder addHeader(String, String);
+    method public abstract org.chromium.net.UrlRequest.Builder allowDirectExecutor();
+    method public abstract org.chromium.net.UrlRequest build();
+    method public abstract org.chromium.net.UrlRequest.Builder disableCache();
+    method public abstract org.chromium.net.UrlRequest.Builder setHttpMethod(String);
+    method public abstract org.chromium.net.UrlRequest.Builder setPriority(int);
+    method public abstract org.chromium.net.UrlRequest.Builder setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor);
+    field public static final int REQUEST_PRIORITY_HIGHEST = 4; // 0x4
+    field public static final int REQUEST_PRIORITY_IDLE = 0; // 0x0
+    field public static final int REQUEST_PRIORITY_LOW = 2; // 0x2
+    field public static final int REQUEST_PRIORITY_LOWEST = 1; // 0x1
+    field public static final int REQUEST_PRIORITY_MEDIUM = 3; // 0x3
+  }
+
+  public abstract static class UrlRequest.Callback {
+    ctor public UrlRequest.Callback();
+    method public void onCanceled(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
+    method public abstract void onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException);
+    method public abstract void onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) throws java.lang.Exception;
+    method public abstract void onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, String) throws java.lang.Exception;
+    method public abstract void onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) throws java.lang.Exception;
+    method public abstract void onSucceeded(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
+  }
+
+  public static class UrlRequest.Status {
+    field public static final int CONNECTING = 10; // 0xa
+    field public static final int DOWNLOADING_PAC_FILE = 5; // 0x5
+    field public static final int ESTABLISHING_PROXY_TUNNEL = 8; // 0x8
+    field public static final int IDLE = 0; // 0x0
+    field public static final int INVALID = -1; // 0xffffffff
+    field public static final int READING_RESPONSE = 14; // 0xe
+    field public static final int RESOLVING_HOST = 9; // 0x9
+    field public static final int RESOLVING_HOST_IN_PAC_FILE = 7; // 0x7
+    field public static final int RESOLVING_PROXY_FOR_URL = 6; // 0x6
+    field public static final int SENDING_REQUEST = 12; // 0xc
+    field public static final int SSL_HANDSHAKE = 11; // 0xb
+    field public static final int WAITING_FOR_AVAILABLE_SOCKET = 2; // 0x2
+    field public static final int WAITING_FOR_CACHE = 4; // 0x4
+    field public static final int WAITING_FOR_DELEGATE = 3; // 0x3
+    field public static final int WAITING_FOR_RESPONSE = 13; // 0xd
+    field public static final int WAITING_FOR_STALLED_SOCKET_POOL = 1; // 0x1
+  }
+
+  public abstract static class UrlRequest.StatusListener {
+    ctor public UrlRequest.StatusListener();
+    method public abstract void onStatus(int);
+  }
+
+  public abstract class UrlResponseInfo {
+    ctor public UrlResponseInfo();
+    method public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> getAllHeaders();
+    method public abstract java.util.List<java.util.Map.Entry<java.lang.String,java.lang.String>> getAllHeadersAsList();
+    method public abstract int getHttpStatusCode();
+    method public abstract String getHttpStatusText();
+    method public abstract String getNegotiatedProtocol();
+    method public abstract String getProxyServer();
+    method public abstract long getReceivedByteCount();
+    method public abstract String getUrl();
+    method public abstract java.util.List<java.lang.String> getUrlChain();
+    method public abstract boolean wasCached();
+  }
+
+}
+
diff --git a/Cronet/api/lint-baseline.txt b/Cronet/api/lint-baseline.txt
new file mode 100644
index 0000000..0e2f25b
--- /dev/null
+++ b/Cronet/api/lint-baseline.txt
@@ -0,0 +1,263 @@
+// Baseline format: 1.0
+AcronymName: org.chromium.net.CronetEngine#createURLStreamHandlerFactory():
+    Acronyms should not be capitalized in method names: was `createURLStreamHandlerFactory`, should this be `createUrlStreamHandlerFactory`?
+
+
+AndroidUri: org.chromium.net.CronetEngine#createURLStreamHandlerFactory():
+    Use android.net.Uri instead of java.net.URL (method org.chromium.net.CronetEngine.createURLStreamHandlerFactory())
+AndroidUri: org.chromium.net.CronetEngine#openConnection(java.net.URL):
+    Use android.net.Uri instead of java.net.URL (method org.chromium.net.CronetEngine.openConnection(java.net.URL))
+AndroidUri: org.chromium.net.CronetEngine#openConnection(java.net.URL) parameter #0:
+    Use android.net.Uri instead of java.net.URL (parameter url in org.chromium.net.CronetEngine.openConnection(java.net.URL url))
+
+
+BuilderSetStyle: org.chromium.net.CronetEngine.Builder#enableBrotli(boolean):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.CronetEngine.Builder.enableBrotli(boolean)
+BuilderSetStyle: org.chromium.net.CronetEngine.Builder#enableHttp2(boolean):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.CronetEngine.Builder.enableHttp2(boolean)
+BuilderSetStyle: org.chromium.net.CronetEngine.Builder#enableHttpCache(int, long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.CronetEngine.Builder.enableHttpCache(int,long)
+BuilderSetStyle: org.chromium.net.CronetEngine.Builder#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.CronetEngine.Builder.enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)
+BuilderSetStyle: org.chromium.net.CronetEngine.Builder#enableQuic(boolean):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.CronetEngine.Builder.enableQuic(boolean)
+BuilderSetStyle: org.chromium.net.UrlRequest.Builder#allowDirectExecutor():
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.UrlRequest.Builder.allowDirectExecutor()
+BuilderSetStyle: org.chromium.net.UrlRequest.Builder#disableCache():
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method org.chromium.net.UrlRequest.Builder.disableCache()
+
+
+ExecutorRegistration: org.chromium.net.UrlRequest#getStatus(org.chromium.net.UrlRequest.StatusListener):
+    Registration methods should have overload that accepts delivery Executor: `getStatus`
+
+
+GenericException: org.chromium.net.UrlRequest.Callback#onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer):
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: org.chromium.net.UrlRequest.Callback#onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, String):
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: org.chromium.net.UrlRequest.Callback#onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo):
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+
+
+GetterOnBuilder: org.chromium.net.CronetEngine.Builder#getDefaultUserAgent():
+    Getter should be on the built object, not the builder: method org.chromium.net.CronetEngine.Builder.getDefaultUserAgent()
+
+
+ListenerInterface: org.chromium.net.UrlRequest.StatusListener:
+    Listeners should be an interface, or otherwise renamed Callback: StatusListener
+
+
+ListenerLast: org.chromium.net.CronetEngine#newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor) parameter #2:
+    Listeners should always be at end of argument list (method `newUrlRequestBuilder`)
+
+
+MissingGetterMatchingBuilder: org.chromium.net.CronetEngine.Builder#addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date):
+    org.chromium.net.CronetEngine does not declare a `getPublicKeyPinss()` method matching method org.chromium.net.CronetEngine.Builder.addPublicKeyPins(String,java.util.Set<byte[]>,boolean,java.util.Date)
+MissingGetterMatchingBuilder: org.chromium.net.CronetEngine.Builder#addQuicHint(String, int, int):
+    org.chromium.net.CronetEngine does not declare a `getQuicHints()` method matching method org.chromium.net.CronetEngine.Builder.addQuicHint(String,int,int)
+MissingGetterMatchingBuilder: org.chromium.net.CronetEngine.Builder#setLibraryLoader(org.chromium.net.CronetEngine.Builder.LibraryLoader):
+    org.chromium.net.CronetEngine does not declare a `getLibraryLoader()` method matching method org.chromium.net.CronetEngine.Builder.setLibraryLoader(org.chromium.net.CronetEngine.Builder.LibraryLoader)
+MissingGetterMatchingBuilder: org.chromium.net.CronetEngine.Builder#setStoragePath(String):
+    org.chromium.net.CronetEngine does not declare a `getStoragePath()` method matching method org.chromium.net.CronetEngine.Builder.setStoragePath(String)
+MissingGetterMatchingBuilder: org.chromium.net.CronetEngine.Builder#setUserAgent(String):
+    org.chromium.net.CronetEngine does not declare a `getUserAgent()` method matching method org.chromium.net.CronetEngine.Builder.setUserAgent(String)
+MissingGetterMatchingBuilder: org.chromium.net.UrlRequest.Builder#addHeader(String, String):
+    org.chromium.net.UrlRequest does not declare a `getHeaders()` method matching method org.chromium.net.UrlRequest.Builder.addHeader(String,String)
+MissingGetterMatchingBuilder: org.chromium.net.UrlRequest.Builder#setHttpMethod(String):
+    org.chromium.net.UrlRequest does not declare a `getHttpMethod()` method matching method org.chromium.net.UrlRequest.Builder.setHttpMethod(String)
+MissingGetterMatchingBuilder: org.chromium.net.UrlRequest.Builder#setPriority(int):
+    org.chromium.net.UrlRequest does not declare a `getPriority()` method matching method org.chromium.net.UrlRequest.Builder.setPriority(int)
+MissingGetterMatchingBuilder: org.chromium.net.UrlRequest.Builder#setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor):
+    org.chromium.net.UrlRequest does not declare a `getUploadDataProvider()` method matching method org.chromium.net.UrlRequest.Builder.setUploadDataProvider(org.chromium.net.UploadDataProvider,java.util.concurrent.Executor)
+
+
+MissingNullability: org.chromium.net.CallbackException#CallbackException(String, Throwable) parameter #0:
+    Missing nullability on parameter `message` in method `CallbackException`
+MissingNullability: org.chromium.net.CallbackException#CallbackException(String, Throwable) parameter #1:
+    Missing nullability on parameter `cause` in method `CallbackException`
+MissingNullability: org.chromium.net.CronetEngine#createURLStreamHandlerFactory():
+    Missing nullability on method `createURLStreamHandlerFactory` return
+MissingNullability: org.chromium.net.CronetEngine#getGlobalMetricsDeltas():
+    Missing nullability on method `getGlobalMetricsDeltas` return
+MissingNullability: org.chromium.net.CronetEngine#getVersionString():
+    Missing nullability on method `getVersionString` return
+MissingNullability: org.chromium.net.CronetEngine#newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor):
+    Missing nullability on method `newUrlRequestBuilder` return
+MissingNullability: org.chromium.net.CronetEngine#newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor) parameter #0:
+    Missing nullability on parameter `url` in method `newUrlRequestBuilder`
+MissingNullability: org.chromium.net.CronetEngine#newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor) parameter #1:
+    Missing nullability on parameter `callback` in method `newUrlRequestBuilder`
+MissingNullability: org.chromium.net.CronetEngine#newUrlRequestBuilder(String, org.chromium.net.UrlRequest.Callback, java.util.concurrent.Executor) parameter #2:
+    Missing nullability on parameter `executor` in method `newUrlRequestBuilder`
+MissingNullability: org.chromium.net.CronetEngine#openConnection(java.net.URL):
+    Missing nullability on method `openConnection` return
+MissingNullability: org.chromium.net.CronetEngine#openConnection(java.net.URL) parameter #0:
+    Missing nullability on parameter `url` in method `openConnection`
+MissingNullability: org.chromium.net.CronetEngine#startNetLogToFile(String, boolean) parameter #0:
+    Missing nullability on parameter `fileName` in method `startNetLogToFile`
+MissingNullability: org.chromium.net.CronetEngine.Builder#Builder(android.content.Context) parameter #0:
+    Missing nullability on parameter `context` in method `Builder`
+MissingNullability: org.chromium.net.CronetEngine.Builder#addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date):
+    Missing nullability on method `addPublicKeyPins` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date) parameter #0:
+    Missing nullability on parameter `hostName` in method `addPublicKeyPins`
+MissingNullability: org.chromium.net.CronetEngine.Builder#addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date) parameter #1:
+    Missing nullability on parameter `pinsSha256` in method `addPublicKeyPins`
+MissingNullability: org.chromium.net.CronetEngine.Builder#addPublicKeyPins(String, java.util.Set<byte[]>, boolean, java.util.Date) parameter #3:
+    Missing nullability on parameter `expirationDate` in method `addPublicKeyPins`
+MissingNullability: org.chromium.net.CronetEngine.Builder#addQuicHint(String, int, int):
+    Missing nullability on method `addQuicHint` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#addQuicHint(String, int, int) parameter #0:
+    Missing nullability on parameter `host` in method `addQuicHint`
+MissingNullability: org.chromium.net.CronetEngine.Builder#build():
+    Missing nullability on method `build` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#enableBrotli(boolean):
+    Missing nullability on method `enableBrotli` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#enableHttp2(boolean):
+    Missing nullability on method `enableHttp2` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#enableHttpCache(int, long):
+    Missing nullability on method `enableHttpCache` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean):
+    Missing nullability on method `enablePublicKeyPinningBypassForLocalTrustAnchors` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#enableQuic(boolean):
+    Missing nullability on method `enableQuic` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#getDefaultUserAgent():
+    Missing nullability on method `getDefaultUserAgent` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#setLibraryLoader(org.chromium.net.CronetEngine.Builder.LibraryLoader):
+    Missing nullability on method `setLibraryLoader` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#setLibraryLoader(org.chromium.net.CronetEngine.Builder.LibraryLoader) parameter #0:
+    Missing nullability on parameter `loader` in method `setLibraryLoader`
+MissingNullability: org.chromium.net.CronetEngine.Builder#setStoragePath(String):
+    Missing nullability on method `setStoragePath` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#setStoragePath(String) parameter #0:
+    Missing nullability on parameter `value` in method `setStoragePath`
+MissingNullability: org.chromium.net.CronetEngine.Builder#setUserAgent(String):
+    Missing nullability on method `setUserAgent` return
+MissingNullability: org.chromium.net.CronetEngine.Builder#setUserAgent(String) parameter #0:
+    Missing nullability on parameter `userAgent` in method `setUserAgent`
+MissingNullability: org.chromium.net.CronetEngine.Builder.LibraryLoader#loadLibrary(String) parameter #0:
+    Missing nullability on parameter `libName` in method `loadLibrary`
+MissingNullability: org.chromium.net.CronetException#CronetException(String, Throwable) parameter #0:
+    Missing nullability on parameter `message` in method `CronetException`
+MissingNullability: org.chromium.net.CronetException#CronetException(String, Throwable) parameter #1:
+    Missing nullability on parameter `cause` in method `CronetException`
+MissingNullability: org.chromium.net.NetworkException#NetworkException(String, Throwable) parameter #0:
+    Missing nullability on parameter `message` in method `NetworkException`
+MissingNullability: org.chromium.net.NetworkException#NetworkException(String, Throwable) parameter #1:
+    Missing nullability on parameter `cause` in method `NetworkException`
+MissingNullability: org.chromium.net.QuicException#QuicException(String, Throwable) parameter #0:
+    Missing nullability on parameter `message` in method `QuicException`
+MissingNullability: org.chromium.net.QuicException#QuicException(String, Throwable) parameter #1:
+    Missing nullability on parameter `cause` in method `QuicException`
+MissingNullability: org.chromium.net.UploadDataProvider#read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer) parameter #0:
+    Missing nullability on parameter `uploadDataSink` in method `read`
+MissingNullability: org.chromium.net.UploadDataProvider#read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer) parameter #1:
+    Missing nullability on parameter `byteBuffer` in method `read`
+MissingNullability: org.chromium.net.UploadDataProvider#rewind(org.chromium.net.UploadDataSink) parameter #0:
+    Missing nullability on parameter `uploadDataSink` in method `rewind`
+MissingNullability: org.chromium.net.UploadDataProviders#create(android.os.ParcelFileDescriptor):
+    Missing nullability on method `create` return
+MissingNullability: org.chromium.net.UploadDataProviders#create(android.os.ParcelFileDescriptor) parameter #0:
+    Missing nullability on parameter `fd` in method `create`
+MissingNullability: org.chromium.net.UploadDataProviders#create(byte[]):
+    Missing nullability on method `create` return
+MissingNullability: org.chromium.net.UploadDataProviders#create(byte[]) parameter #0:
+    Missing nullability on parameter `data` in method `create`
+MissingNullability: org.chromium.net.UploadDataProviders#create(byte[], int, int):
+    Missing nullability on method `create` return
+MissingNullability: org.chromium.net.UploadDataProviders#create(byte[], int, int) parameter #0:
+    Missing nullability on parameter `data` in method `create`
+MissingNullability: org.chromium.net.UploadDataProviders#create(java.io.File):
+    Missing nullability on method `create` return
+MissingNullability: org.chromium.net.UploadDataProviders#create(java.io.File) parameter #0:
+    Missing nullability on parameter `file` in method `create`
+MissingNullability: org.chromium.net.UploadDataProviders#create(java.nio.ByteBuffer):
+    Missing nullability on method `create` return
+MissingNullability: org.chromium.net.UploadDataProviders#create(java.nio.ByteBuffer) parameter #0:
+    Missing nullability on parameter `buffer` in method `create`
+MissingNullability: org.chromium.net.UploadDataSink#onReadError(Exception) parameter #0:
+    Missing nullability on parameter `exception` in method `onReadError`
+MissingNullability: org.chromium.net.UploadDataSink#onRewindError(Exception) parameter #0:
+    Missing nullability on parameter `exception` in method `onRewindError`
+MissingNullability: org.chromium.net.UrlRequest#getStatus(org.chromium.net.UrlRequest.StatusListener) parameter #0:
+    Missing nullability on parameter `listener` in method `getStatus`
+MissingNullability: org.chromium.net.UrlRequest#read(java.nio.ByteBuffer) parameter #0:
+    Missing nullability on parameter `buffer` in method `read`
+MissingNullability: org.chromium.net.UrlRequest.Builder#addHeader(String, String):
+    Missing nullability on method `addHeader` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#addHeader(String, String) parameter #0:
+    Missing nullability on parameter `header` in method `addHeader`
+MissingNullability: org.chromium.net.UrlRequest.Builder#addHeader(String, String) parameter #1:
+    Missing nullability on parameter `value` in method `addHeader`
+MissingNullability: org.chromium.net.UrlRequest.Builder#allowDirectExecutor():
+    Missing nullability on method `allowDirectExecutor` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#build():
+    Missing nullability on method `build` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#disableCache():
+    Missing nullability on method `disableCache` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#setHttpMethod(String):
+    Missing nullability on method `setHttpMethod` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#setHttpMethod(String) parameter #0:
+    Missing nullability on parameter `method` in method `setHttpMethod`
+MissingNullability: org.chromium.net.UrlRequest.Builder#setPriority(int):
+    Missing nullability on method `setPriority` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor):
+    Missing nullability on method `setUploadDataProvider` return
+MissingNullability: org.chromium.net.UrlRequest.Builder#setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor) parameter #0:
+    Missing nullability on parameter `uploadDataProvider` in method `setUploadDataProvider`
+MissingNullability: org.chromium.net.UrlRequest.Builder#setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor) parameter #1:
+    Missing nullability on parameter `executor` in method `setUploadDataProvider`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onCanceled(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #0:
+    Missing nullability on parameter `request` in method `onCanceled`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onCanceled(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #1:
+    Missing nullability on parameter `info` in method `onCanceled`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException) parameter #0:
+    Missing nullability on parameter `request` in method `onFailed`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException) parameter #1:
+    Missing nullability on parameter `info` in method `onFailed`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException) parameter #2:
+    Missing nullability on parameter `error` in method `onFailed`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) parameter #0:
+    Missing nullability on parameter `request` in method `onReadCompleted`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) parameter #1:
+    Missing nullability on parameter `info` in method `onReadCompleted`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) parameter #2:
+    Missing nullability on parameter `byteBuffer` in method `onReadCompleted`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, String) parameter #0:
+    Missing nullability on parameter `request` in method `onRedirectReceived`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, String) parameter #1:
+    Missing nullability on parameter `info` in method `onRedirectReceived`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, String) parameter #2:
+    Missing nullability on parameter `newLocationUrl` in method `onRedirectReceived`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #0:
+    Missing nullability on parameter `request` in method `onResponseStarted`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #1:
+    Missing nullability on parameter `info` in method `onResponseStarted`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onSucceeded(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #0:
+    Missing nullability on parameter `request` in method `onSucceeded`
+MissingNullability: org.chromium.net.UrlRequest.Callback#onSucceeded(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) parameter #1:
+    Missing nullability on parameter `info` in method `onSucceeded`
+MissingNullability: org.chromium.net.UrlResponseInfo#getAllHeaders():
+    Missing nullability on method `getAllHeaders` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getAllHeadersAsList():
+    Missing nullability on method `getAllHeadersAsList` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getHttpStatusText():
+    Missing nullability on method `getHttpStatusText` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getNegotiatedProtocol():
+    Missing nullability on method `getNegotiatedProtocol` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getProxyServer():
+    Missing nullability on method `getProxyServer` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getUrl():
+    Missing nullability on method `getUrl` return
+MissingNullability: org.chromium.net.UrlResponseInfo#getUrlChain():
+    Missing nullability on method `getUrlChain` return
+
+
+NotCloseable: org.chromium.net.CronetEngine:
+    Classes that release resources (shutdown()) should implement AutoClosable and CloseGuard: class org.chromium.net.CronetEngine
+
+
+StaticFinalBuilder: org.chromium.net.CronetEngine.Builder:
+    Builder must be final: org.chromium.net.CronetEngine.Builder
+StaticFinalBuilder: org.chromium.net.UrlRequest.Builder:
+    Builder must be final: org.chromium.net.UrlRequest.Builder
diff --git a/Cronet/api/module-lib-current.txt b/Cronet/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/module-lib-removed.txt b/Cronet/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/removed.txt b/Cronet/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/system-current.txt b/Cronet/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/system-removed.txt b/Cronet/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/test-current.txt b/Cronet/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/api/test-removed.txt b/Cronet/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/Cronet/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/Cronet/jarjar-rules.txt b/Cronet/jarjar-rules.txt
new file mode 100644
index 0000000..7111ebc
--- /dev/null
+++ b/Cronet/jarjar-rules.txt
@@ -0,0 +1,3 @@
+rule androidx.** com.android.cronet.@0
+rule android.support.** com.android.cronet.@0
+
diff --git a/Cronet/prebuilt/VERSION b/Cronet/prebuilt/VERSION
new file mode 100644
index 0000000..c16ab94
--- /dev/null
+++ b/Cronet/prebuilt/VERSION
@@ -0,0 +1,4 @@
+MAJOR=80
+MINOR=0
+BUILD=3986
+PATCH=0
diff --git a/Cronet/prebuilt/api_version.txt b/Cronet/prebuilt/api_version.txt
new file mode 100644
index 0000000..48082f7
--- /dev/null
+++ b/Cronet/prebuilt/api_version.txt
@@ -0,0 +1 @@
+12
diff --git a/Cronet/prebuilt/cronet_api-src.jar b/Cronet/prebuilt/cronet_api-src.jar
new file mode 100644
index 0000000..924b877
--- /dev/null
+++ b/Cronet/prebuilt/cronet_api-src.jar
Binary files differ
diff --git a/Cronet/prebuilt/cronet_api.jar b/Cronet/prebuilt/cronet_api.jar
new file mode 100644
index 0000000..977b28d
--- /dev/null
+++ b/Cronet/prebuilt/cronet_api.jar
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_common_java.jar b/Cronet/prebuilt/cronet_impl_common_java.jar
new file mode 100644
index 0000000..fa71bf3
--- /dev/null
+++ b/Cronet/prebuilt/cronet_impl_common_java.jar
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_native_java.jar b/Cronet/prebuilt/cronet_impl_native_java.jar
new file mode 100644
index 0000000..4cdd6f3
--- /dev/null
+++ b/Cronet/prebuilt/cronet_impl_native_java.jar
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_platform_java.jar b/Cronet/prebuilt/cronet_impl_platform_java.jar
new file mode 100644
index 0000000..6d6042f
--- /dev/null
+++ b/Cronet/prebuilt/cronet_impl_platform_java.jar
Binary files differ
diff --git a/Cronet/prebuilt/libs/arm64-v8a/libcronet.107.0.5284.2.so b/Cronet/prebuilt/libs/arm64-v8a/libcronet.107.0.5284.2.so
new file mode 100644
index 0000000..7f2540a
--- /dev/null
+++ b/Cronet/prebuilt/libs/arm64-v8a/libcronet.107.0.5284.2.so
Binary files differ
diff --git a/Cronet/prebuilt/libs/armeabi-v7a/libcronet.107.0.5284.2.so b/Cronet/prebuilt/libs/armeabi-v7a/libcronet.107.0.5284.2.so
new file mode 100644
index 0000000..115429a
--- /dev/null
+++ b/Cronet/prebuilt/libs/armeabi-v7a/libcronet.107.0.5284.2.so
Binary files differ
diff --git a/Cronet/prebuilt/libs/x86/libcronet.107.0.5284.2.so b/Cronet/prebuilt/libs/x86/libcronet.107.0.5284.2.so
new file mode 100644
index 0000000..27923f7
--- /dev/null
+++ b/Cronet/prebuilt/libs/x86/libcronet.107.0.5284.2.so
Binary files differ
diff --git a/Cronet/prebuilt/libs/x86_64/libcronet.107.0.5284.2.so b/Cronet/prebuilt/libs/x86_64/libcronet.107.0.5284.2.so
new file mode 100644
index 0000000..803e5cd
--- /dev/null
+++ b/Cronet/prebuilt/libs/x86_64/libcronet.107.0.5284.2.so
Binary files differ
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
new file mode 100644
index 0000000..e71c707
--- /dev/null
+++ b/Cronet/tests/cts/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "CronetApiCommonTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.core_core",
+        "junit",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+        "androidx.annotation_annotation",
+        "framework-cronet",
+    ],
+}
+
+android_test {
+    name: "CronetApiTest",
+    sdk_version: "test_current",
+    test_suites: ["device-tests"],
+    certificate: "platform",
+    static_libs: [
+        "CronetApiCommonTests",
+    ],
+}
diff --git a/Cronet/tests/cts/AndroidManifest.xml b/Cronet/tests/cts/AndroidManifest.xml
new file mode 100644
index 0000000..db0f0b3
--- /dev/null
+++ b/Cronet/tests/cts/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="org.chromium.net.test">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="framework-cronet" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="org.chromium.net.test"
+        android:label="Cronet API Networking Tests" />
+</manifest>
\ No newline at end of file
diff --git a/Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java b/Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java
new file mode 100644
index 0000000..7dd9a9a
--- /dev/null
+++ b/Cronet/tests/cts/src/org/chromium/net/test/CronetUrlRequestTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.chromium.net.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.chromium.net.CronetEngine;
+import org.chromium.net.UrlRequest;
+import org.chromium.net.UrlResponseInfo;
+import org.chromium.net.test.util.TestUrlRequestCallback;
+import org.chromium.net.test.util.TestUrlRequestCallback.ResponseStep;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class CronetUrlRequestTest {
+    private static final String TAG = CronetUrlRequestTest.class.getSimpleName();
+    private static final String HTTPS_PREFIX = "https://";
+
+    private final String[] mTestDomains = {"www.google.com", "www.android.com"};
+    @NonNull private CronetEngine mCronetEngine;
+    @NonNull private ConnectivityManager mCm;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        CronetEngine.Builder builder = new CronetEngine.Builder(context);
+        builder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
+                .enableHttp2(true)
+                // .enableBrotli(true)
+                .enableQuic(true);
+        mCronetEngine = builder.build();
+    }
+
+    private static void assertGreaterThan(String msg, int first, int second) {
+        assertTrue(msg + " Excepted " + first + " to be greater than " + second, first > second);
+    }
+
+    private void assertHasTestableNetworks() {
+        assertNotNull("This test requires a working Internet connection", mCm.getActiveNetwork());
+    }
+
+    private String getRandomDomain() {
+        int index = (new Random()).nextInt(mTestDomains.length);
+        return mTestDomains[index];
+    }
+
+    @Test
+    public void testUrlRequestGet_CompletesSuccessfully() throws Exception {
+        assertHasTestableNetworks();
+        String url = HTTPS_PREFIX + getRandomDomain();
+        TestUrlRequestCallback callback = new TestUrlRequestCallback();
+        UrlRequest.Builder builder = mCronetEngine.newUrlRequestBuilder(url, callback,
+                callback.getExecutor());
+        builder.build().start();
+
+        callback.expectCallback(ResponseStep.ON_SUCCEEDED);
+
+        UrlResponseInfo info = callback.mResponseInfo;
+        assertEquals("Unexpected http status code from " + url + ".", 200,
+                info.getHttpStatusCode());
+        assertGreaterThan(
+                "Received byte from " + url + " is 0.", (int) info.getReceivedByteCount(), 0);
+    }
+}
diff --git a/Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java b/Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java
new file mode 100644
index 0000000..3c7c001
--- /dev/null
+++ b/Cronet/tests/cts/src/org/chromium/net/test/util/TestUrlRequestCallback.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2022 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 org.chromium.net.test.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.ConditionVariable;
+import android.os.StrictMode;
+
+import org.chromium.net.CallbackException;
+import org.chromium.net.CronetException;
+import org.chromium.net.InlineExecutionProhibitedException;
+import org.chromium.net.UrlRequest;
+import org.chromium.net.UrlResponseInfo;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Callback that tracks information from different callbacks and has a
+ * method to block thread until the request completes on another thread.
+ * Allows us to cancel, block request or throw an exception from an arbitrary step.
+ */
+public class TestUrlRequestCallback extends UrlRequest.Callback {
+    private static final int TIMEOUT_MS = 12_000;
+    public ArrayList<UrlResponseInfo> mRedirectResponseInfoList = new ArrayList<>();
+    public ArrayList<String> mRedirectUrlList = new ArrayList<>();
+    public UrlResponseInfo mResponseInfo;
+    public CronetException mError;
+
+    public ResponseStep mResponseStep = ResponseStep.NOTHING;
+
+    public int mRedirectCount;
+    public boolean mOnErrorCalled;
+    public boolean mOnCanceledCalled;
+
+    public int mHttpResponseDataLength;
+    public String mResponseAsString = "";
+
+    public int mReadBufferSize = 32 * 1024;
+
+    // When false, the consumer is responsible for all calls into the request
+    // that advance it.
+    private boolean mAutoAdvance = true;
+    // Whether an exception is thrown by maybeThrowCancelOrPause().
+    private boolean mCallbackExceptionThrown;
+
+    // Whether to permit calls on the network thread.
+    private boolean mAllowDirectExecutor;
+
+    // Whether to stop the executor thread after reaching a terminal method.
+    // Terminal methods are (onSucceeded, onFailed or onCancelled)
+    private boolean mBlockOnTerminalState;
+
+    // Conditionally fail on certain steps.
+    private FailureType mFailureType = FailureType.NONE;
+    private ResponseStep mFailureStep = ResponseStep.NOTHING;
+
+    // Signals when request is done either successfully or not.
+    private final ConditionVariable mDone = new ConditionVariable();
+
+    // Hangs the calling thread until a terminal method has started executing.
+    private final ConditionVariable mWaitForTerminalToStart = new ConditionVariable();
+
+    // Signaled on each step when mAutoAdvance is false.
+    private final ConditionVariable mStepBlock = new ConditionVariable();
+
+    // Executor Service for Cronet callbacks.
+    private final ExecutorService mExecutorService;
+    private Thread mExecutorThread;
+
+    // position() of ByteBuffer prior to read() call.
+    private int mBufferPositionBeforeRead;
+
+    private static class ExecutorThreadFactory implements ThreadFactory {
+        @Override
+        public Thread newThread(final Runnable r) {
+            return new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    StrictMode.ThreadPolicy threadPolicy = StrictMode.getThreadPolicy();
+                    try {
+                        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                                .detectNetwork()
+                                .penaltyLog()
+                                .penaltyDeath()
+                                .build());
+                        r.run();
+                    } finally {
+                        StrictMode.setThreadPolicy(threadPolicy);
+                    }
+                }
+            });
+        }
+    }
+
+    public enum ResponseStep {
+        NOTHING,
+        ON_RECEIVED_REDIRECT,
+        ON_RESPONSE_STARTED,
+        ON_READ_COMPLETED,
+        ON_SUCCEEDED,
+        ON_FAILED,
+        ON_CANCELED,
+    }
+
+    public enum FailureType {
+        NONE,
+        CANCEL_SYNC,
+        CANCEL_ASYNC,
+        // Same as above, but continues to advance the request after posting
+        // the cancellation task.
+        CANCEL_ASYNC_WITHOUT_PAUSE,
+        THROW_SYNC
+    }
+
+    private static void assertContains(String expectedSubstring, String actualString) {
+        assertNotNull(actualString);
+        assertTrue("String [" + actualString + "] doesn't contain substring [" + expectedSubstring
+                + "]", actualString.contains(expectedSubstring));
+
+    }
+
+    /**
+     * Set {@code mExecutorThread}.
+     */
+    private void fillInExecutorThread() {
+        mExecutorService.execute(new Runnable() {
+            @Override
+            public void run() {
+                mExecutorThread = Thread.currentThread();
+            }
+        });
+    }
+
+    private boolean isTerminalCallback(ResponseStep step) {
+        switch (step) {
+            case ON_SUCCEEDED:
+            case ON_CANCELED:
+            case ON_FAILED:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Create a {@link TestUrlRequestCallback} with a new single-threaded executor.
+     */
+    public TestUrlRequestCallback() {
+        this(Executors.newSingleThreadExecutor(new ExecutorThreadFactory()));
+    }
+
+    /**
+     * Create a {@link TestUrlRequestCallback} using a custom single-threaded executor.
+     */
+    public TestUrlRequestCallback(ExecutorService executorService) {
+        mExecutorService = executorService;
+        fillInExecutorThread();
+    }
+
+    /**
+     * This blocks the callback executor thread once it has reached a final state callback.
+     * In order to continue execution, this method must be called again and providing {@code false}
+     * to continue execution.
+     *
+     * @param blockOnTerminalState the state to set for the executor thread
+     */
+    public void setBlockOnTerminalState(boolean blockOnTerminalState) {
+        mBlockOnTerminalState = blockOnTerminalState;
+        if (!blockOnTerminalState) {
+            mDone.open();
+        }
+    }
+
+    public void setAutoAdvance(boolean autoAdvance) {
+        mAutoAdvance = autoAdvance;
+    }
+
+    public void setAllowDirectExecutor(boolean allowed) {
+        mAllowDirectExecutor = allowed;
+    }
+
+    public void setFailure(FailureType failureType, ResponseStep failureStep) {
+        mFailureStep = failureStep;
+        mFailureType = failureType;
+    }
+
+    /**
+     * Blocks the calling thread till callback execution is done
+     *
+     * @return true if the condition was opened, false if the call returns because of the timeout.
+     */
+    public boolean blockForDone() {
+        return mDone.block(TIMEOUT_MS);
+    }
+
+    /**
+     * Waits for a terminal callback to complete execution before failing if the callback
+     * is not the expected one
+     *
+     * @param expectedStep the expected callback step
+     */
+    public void expectCallback(ResponseStep expectedStep) {
+        if (isTerminalCallback(expectedStep)) {
+            assertTrue("Did not receive terminal callback before timeout", blockForDone());
+        }
+        assertSame(expectedStep, mResponseStep);
+    }
+
+    /**
+     * Blocks the calling thread until one of the final states has been called.
+     * This is called before the callback has finished executed.
+     */
+    public void waitForTerminalToStart() {
+        mWaitForTerminalToStart.block();
+    }
+
+    public void waitForNextStep() {
+        mStepBlock.block();
+        mStepBlock.close();
+    }
+
+    public ExecutorService getExecutor() {
+        return mExecutorService;
+    }
+
+    public void shutdownExecutor() {
+        mExecutorService.shutdown();
+    }
+
+    /**
+     * Shuts down the ExecutorService and waits until it executes all posted
+     * tasks.
+     */
+    public void shutdownExecutorAndWait() {
+        mExecutorService.shutdown();
+        try {
+            // Termination shouldn't take long. Use 1 min which should be more than enough.
+            mExecutorService.awaitTermination(1, TimeUnit.MINUTES);
+        } catch (InterruptedException e) {
+            fail("ExecutorService is interrupted while waiting for termination");
+        }
+        assertTrue(mExecutorService.isTerminated());
+    }
+
+    @Override
+    public void onRedirectReceived(
+            UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
+        checkExecutorThread();
+        assertFalse(request.isDone());
+        assertTrue(mResponseStep == ResponseStep.NOTHING
+                || mResponseStep == ResponseStep.ON_RECEIVED_REDIRECT);
+        assertNull(mError);
+
+        mResponseStep = ResponseStep.ON_RECEIVED_REDIRECT;
+        mRedirectUrlList.add(newLocationUrl);
+        mRedirectResponseInfoList.add(info);
+        ++mRedirectCount;
+        if (maybeThrowCancelOrPause(request)) {
+            return;
+        }
+        request.followRedirect();
+    }
+
+    @Override
+    public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
+        checkExecutorThread();
+        assertFalse(request.isDone());
+        assertTrue(mResponseStep == ResponseStep.NOTHING
+                || mResponseStep == ResponseStep.ON_RECEIVED_REDIRECT);
+        assertNull(mError);
+
+        mResponseStep = ResponseStep.ON_RESPONSE_STARTED;
+        mResponseInfo = info;
+        if (maybeThrowCancelOrPause(request)) {
+            return;
+        }
+        startNextRead(request);
+    }
+
+    @Override
+    public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
+        checkExecutorThread();
+        assertFalse(request.isDone());
+        assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
+                || mResponseStep == ResponseStep.ON_READ_COMPLETED);
+        assertNull(mError);
+
+        mResponseStep = ResponseStep.ON_READ_COMPLETED;
+
+        final byte[] lastDataReceivedAsBytes;
+        final int bytesRead = byteBuffer.position() - mBufferPositionBeforeRead;
+        mHttpResponseDataLength += bytesRead;
+        lastDataReceivedAsBytes = new byte[bytesRead];
+        // Rewind |byteBuffer.position()| to pre-read() position.
+        byteBuffer.position(mBufferPositionBeforeRead);
+        // This restores |byteBuffer.position()| to its value on entrance to
+        // this function.
+        byteBuffer.get(lastDataReceivedAsBytes);
+        mResponseAsString += new String(lastDataReceivedAsBytes);
+
+        if (maybeThrowCancelOrPause(request)) {
+            return;
+        }
+        startNextRead(request);
+    }
+
+    @Override
+    public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
+        checkExecutorThread();
+        assertTrue(request.isDone());
+        assertTrue(mResponseStep == ResponseStep.ON_RESPONSE_STARTED
+                || mResponseStep == ResponseStep.ON_READ_COMPLETED);
+        assertFalse(mOnErrorCalled);
+        assertFalse(mOnCanceledCalled);
+        assertNull(mError);
+
+        mResponseStep = ResponseStep.ON_SUCCEEDED;
+        mResponseInfo = info;
+        mWaitForTerminalToStart.open();
+        if (mBlockOnTerminalState) mDone.block();
+        openDone();
+        maybeThrowCancelOrPause(request);
+    }
+
+    @Override
+    public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
+        // If the failure is because of prohibited direct execution, the test shouldn't fail
+        // since the request already did.
+        if (error.getCause() instanceof InlineExecutionProhibitedException) {
+            mAllowDirectExecutor = true;
+        }
+        checkExecutorThread();
+        assertTrue(request.isDone());
+        // Shouldn't happen after success.
+        assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED);
+        // Should happen at most once for a single request.
+        assertFalse(mOnErrorCalled);
+        assertFalse(mOnCanceledCalled);
+        assertNull(mError);
+        if (mCallbackExceptionThrown) {
+            assertTrue(error instanceof CallbackException);
+            assertContains("Exception received from UrlRequest.Callback", error.getMessage());
+            assertNotNull(error.getCause());
+            assertTrue(error.getCause() instanceof IllegalStateException);
+            assertContains("Listener Exception.", error.getCause().getMessage());
+        }
+
+        mResponseStep = ResponseStep.ON_FAILED;
+        mOnErrorCalled = true;
+        mError = error;
+        mWaitForTerminalToStart.open();
+        if (mBlockOnTerminalState) mDone.block();
+        openDone();
+        maybeThrowCancelOrPause(request);
+    }
+
+    @Override
+    public void onCanceled(UrlRequest request, UrlResponseInfo info) {
+        checkExecutorThread();
+        assertTrue(request.isDone());
+        // Should happen at most once for a single request.
+        assertFalse(mOnCanceledCalled);
+        assertFalse(mOnErrorCalled);
+        assertNull(mError);
+
+        mResponseStep = ResponseStep.ON_CANCELED;
+        mOnCanceledCalled = true;
+        mWaitForTerminalToStart.open();
+        if (mBlockOnTerminalState) mDone.block();
+        openDone();
+        maybeThrowCancelOrPause(request);
+    }
+
+    public void startNextRead(UrlRequest request) {
+        startNextRead(request, ByteBuffer.allocateDirect(mReadBufferSize));
+    }
+
+    public void startNextRead(UrlRequest request, ByteBuffer buffer) {
+        mBufferPositionBeforeRead = buffer.position();
+        request.read(buffer);
+    }
+
+    public boolean isDone() {
+        // It's not mentioned by the Android docs, but block(0) seems to block
+        // indefinitely, so have to block for one millisecond to get state
+        // without blocking.
+        return mDone.block(1);
+    }
+
+    protected void openDone() {
+        mDone.open();
+    }
+
+    private void checkExecutorThread() {
+        if (!mAllowDirectExecutor) {
+            assertEquals(mExecutorThread, Thread.currentThread());
+        }
+    }
+
+    /**
+     * Returns {@code false} if the listener should continue to advance the
+     * request.
+     */
+    private boolean maybeThrowCancelOrPause(final UrlRequest request) {
+        checkExecutorThread();
+        if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) {
+            if (!mAutoAdvance) {
+                mStepBlock.open();
+                return true;
+            }
+            return false;
+        }
+
+        if (mFailureType == FailureType.THROW_SYNC) {
+            assertFalse(mCallbackExceptionThrown);
+            mCallbackExceptionThrown = true;
+            throw new IllegalStateException("Listener Exception.");
+        }
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                request.cancel();
+            }
+        };
+        if (mFailureType == FailureType.CANCEL_ASYNC
+                || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) {
+            getExecutor().execute(task);
+        } else {
+            task.run();
+        }
+        return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE;
+    }
+}
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index bc1d002..3a08422 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -10,6 +10,7 @@
 markchien@google.com
 martinwu@google.com
 maze@google.com
+motomuman@google.com
 nuccachen@google.com
 paulhu@google.com
 prohr@google.com
diff --git a/OWNERS_core_networking_xts b/OWNERS_core_networking_xts
index a6627fe..1844334 100644
--- a/OWNERS_core_networking_xts
+++ b/OWNERS_core_networking_xts
@@ -1,2 +1,7 @@
 lorenzo@google.com
-satk@google.com
+satk@google.com #{LAST_RESORT_SUGGESTION}
+
+# For cherry-picks of CLs that are already merged in aosp/master, or flaky test fixes.
+jchalard@google.com #{LAST_RESORT_SUGGESTION}
+maze@google.com #{LAST_RESORT_SUGGESTION}
+reminv@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1e8babf..700a085 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -135,6 +135,37 @@
         }
       ]
     },
+    // Test with APK modules only, in cases where APEX is not supported, or the other modules
+    // were simply not updated
+    {
+      "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        },
+        {
+          "exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
+        }
+      ]
+    },
+    // Test with connectivity/tethering module only, to catch integration issues with older versions
+    // of other modules. "new tethering + old NetworkStack" is not a configuration that should
+    // really exist in the field, but there is no strong guarantee, and it is required by MTS
+    // testing for module qualification, where modules are tested independently.
+    {
+      "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        }
+      ]
+    },
     {
       "name": "bpf_existence_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
     },
@@ -159,38 +190,6 @@
     {
       "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
       "keywords": ["sim"]
-    },
-    // TODO: move to mainline-presubmit when known green.
-    // Test with APK modules only, in cases where APEX is not supported, or the other modules were simply not updated
-    {
-      "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk]",
-      "options": [
-        {
-          "exclude-annotation": "com.android.testutils.SkipPresubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.RequiresDevice"
-        },
-        {
-          "exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
-        }
-      ]
-    },
-    // TODO: move to mainline-presubmit when known green.
-    // Test with connectivity/tethering module only, to catch integration issues with older versions of other modules.
-    // "new tethering + old NetworkStack" is not a configuration that should really exist in the field, but
-    // there is no strong guarantee, and it is required by MTS testing for module qualification, where modules
-    // are tested independently.
-    {
-      "name": "CtsNetTestCasesLatestSdk[com.google.android.tethering.apex]",
-      "options": [
-        {
-          "exclude-annotation": "com.android.testutils.SkipPresubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.RequiresDevice"
-        }
-      ]
     }
   ],
   "imports": [
@@ -205,6 +204,9 @@
     },
     {
       "path": "packages/modules/CaptivePortalLogin"
+    },
+    {
+      "path": "vendor/xts/gts-tests/hostsidetests/networkstack"
     }
   ]
 }
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 3ab1ec2..829e66a 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -21,15 +21,20 @@
 java_defaults {
     name: "TetheringApiLevel",
     sdk_version: "module_current",
-    target_sdk_version: "33",
     min_sdk_version: "30",
 }
 
 java_defaults {
+    name: "TetheringReleaseTargetSdk",
+    target_sdk_version: "33",
+}
+
+java_defaults {
     name: "TetheringExternalLibs",
     // Libraries not including Tethering's own framework-tethering (different flavors of that one
     // are needed depending on the build rule)
     libs: [
+        "framework-configinfrastructure",
         "framework-connectivity.stubs.module_lib",
         "framework-connectivity-t.stubs.module_lib",
         "framework-statsd.stubs.module_lib",
@@ -70,7 +75,6 @@
     libs: [
         "framework-tethering.impl",
     ],
-    plugins: ["java_api_finder"],
     manifest: "AndroidManifestBase.xml",
     lint: { strict_updatability_linting: true },
 }
@@ -81,7 +85,8 @@
     defaults: [
         "ConnectivityNextEnableDefaults",
         "TetheringAndroidLibraryDefaults",
-        "TetheringApiLevel"
+        "TetheringApiLevel",
+        "TetheringReleaseTargetSdk"
     ],
     static_libs: [
         "NetworkStackApiCurrentShims",
@@ -94,7 +99,8 @@
     name: "TetheringApiStableLib",
     defaults: [
         "TetheringAndroidLibraryDefaults",
-        "TetheringApiLevel"
+        "TetheringApiLevel",
+        "TetheringReleaseTargetSdk"
     ],
     static_libs: [
         "NetworkStackApiStableShims",
@@ -180,7 +186,12 @@
 // Non-updatable tethering running in the system server process for devices not using the module
 android_app {
     name: "InProcessTethering",
-    defaults: ["TetheringAppDefaults", "TetheringApiLevel", "ConnectivityNextEnableDefaults"],
+    defaults: [
+        "TetheringAppDefaults",
+        "TetheringApiLevel",
+        "ConnectivityNextEnableDefaults",
+        "TetheringReleaseTargetSdk"
+    ],
     static_libs: ["TetheringApiCurrentLib"],
     certificate: "platform",
     manifest: "AndroidManifest_InProcess.xml",
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 3b5d6bf..e59c8e4 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -54,6 +54,7 @@
     name: "com.android.tethering",
     defaults: [
         "ConnectivityApexDefaults",
+        "CronetApexDefaults",
         "r-launched-apex-module",
     ],
     compile_multilib: "both",
@@ -69,7 +70,10 @@
                 "libservice-connectivity",
                 "libandroid_net_connectivity_com_android_net_module_util_jni",
             ],
-            native_shared_libs: ["libnetd_updatable"],
+            native_shared_libs: [
+                "libcom.android.tethering.connectivity_native",
+                "libnetd_updatable",
+            ],
         },
         both: {
             jni_libs: [
@@ -128,7 +132,10 @@
 
 filegroup {
     name: "connectivity-hiddenapi-files",
-    srcs: ["hiddenapi/*.txt"],
+    srcs: [
+        ":connectivity-t-hiddenapi-files",
+        "hiddenapi/*.txt",
+    ],
     visibility: ["//packages/modules/Connectivity:__subpackages__"],
 }
 
@@ -159,16 +166,11 @@
     // Additional hidden API flag files to override the defaults. This must only be
     // modified by the Soong or platform compat team.
     hidden_api: {
-        max_target_r_low_priority: [
-            "hiddenapi/hiddenapi-max-target-r-loprio.txt",
-        ],
         max_target_o_low_priority: [
             "hiddenapi/hiddenapi-max-target-o-low-priority.txt",
-            "hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt",
         ],
         unsupported: [
             "hiddenapi/hiddenapi-unsupported.txt",
-            "hiddenapi/hiddenapi-unsupported-tiramisu.txt",
         ],
 
         // The following packages contain classes from other modules on the
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
index 89f3813..168d7f9 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
@@ -21,17 +21,10 @@
  * @hide
  */
 parcelable TetheringConfigurationParcel {
-    int subId;
     String[] tetherableUsbRegexs;
     String[] tetherableWifiRegexs;
     String[] tetherableBluetoothRegexs;
-    boolean isDunRequired;
-    boolean chooseUpstreamAutomatically;
-    int[] preferredUpstreamIfaceTypes;
     String[] legacyDhcpRanges;
-    String[] defaultIPv4DNS;
-    boolean enableLegacyDhcpServer;
     String[] provisioningApp;
     String provisioningAppNoUi;
-    int provisioningCheckPeriod;
 }
diff --git a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
index 291bf54..6699c0d 100644
--- a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
@@ -49,8 +49,10 @@
         BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kICMPv6TypeOffset),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    type, 0, 1),
 
-        // Accept or reject.
+        // Accept.
         BPF_STMT(BPF_RET | BPF_K,              0xffff),
+
+        // Reject.
         BPF_STMT(BPF_RET | BPF_K,              0)
     };
 
diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags
index 2905e28..109bbda 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -1,7 +1,10 @@
 # Keep class's integer static field for MessageUtils to parsing their name.
--keep class com.android.networkstack.tethering.Tethering$TetherMainSM {
-    static final int CMD_*;
-    static final int EVENT_*;
+-keepclassmembers class com.android.server.**,android.net.**,com.android.networkstack.** {
+    static final % POLICY_*;
+    static final % NOTIFY_TYPE_*;
+    static final % TRANSPORT_*;
+    static final % CMD_*;
+    static final % EVENT_*;
 }
 
 -keep class com.android.networkstack.tethering.util.BpfMap {
@@ -21,12 +24,8 @@
     *;
 }
 
--keepclassmembers class android.net.ip.IpServer {
-    static final int CMD_*;
-}
-
 # The lite proto runtime uses reflection to access fields based on the names in
 # the schema, keep all the fields.
 -keepclassmembers class * extends com.android.networkstack.tethering.protobuf.MessageLite {
     <fields>;
-}
\ No newline at end of file
+}
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 438b592..8cf13d3 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -378,6 +378,11 @@
         return Collections.unmodifiableList(mDhcpLeases);
     }
 
+    /** Enable this IpServer. IpServer state machine will be tethered or localHotspot state. */
+    public void enable(final int requestedState, final TetheringRequestParcel request) {
+        sendMessage(CMD_TETHER_REQUESTED, requestedState, 0, request);
+    }
+
     /** Stop this IpServer. After this is called this IpServer should not be used any more. */
     public void stop() {
         sendMessage(CMD_INTERFACE_DOWN);
@@ -1146,9 +1151,6 @@
                 case CMD_INTERFACE_DOWN:
                     transitionTo(mUnavailableState);
                     break;
-                case CMD_IPV6_TETHER_UPDATE:
-                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 142a0b9..6a5089d 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -403,6 +403,18 @@
                 return null;
             }
         }
+
+        /** Get error BPF map. */
+        @Nullable public IBpfMap<S32, S32> getBpfErrorMap() {
+            if (!isAtLeastS()) return null;
+            try {
+                return new BpfMap<>(TETHER_ERROR_MAP_PATH,
+                    BpfMap.BPF_F_RDONLY, S32.class, S32.class);
+            } catch (ErrnoException e) {
+                Log.e(TAG, "Cannot create error map: " + e);
+                return null;
+            }
+        }
     }
 
     @VisibleForTesting
@@ -536,6 +548,13 @@
         // TODO: Wrap conntrackMonitor stopping function into mBpfCoordinatorShim.
         if (!isUsingBpf() || !mDeps.isAtLeastS()) return;
 
+        // Ignore stopping monitoring if the monitor has never started for a given IpServer.
+        if (!mMonitoringIpServers.contains(ipServer)) {
+            mLog.e("Ignore stopping monitoring because monitoring has never started for "
+                    + ipServer.interfaceName());
+            return;
+        }
+
         mMonitoringIpServers.remove(ipServer);
 
         if (!mMonitoringIpServers.isEmpty()) return;
@@ -1280,13 +1299,15 @@
     }
 
     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
-        if (!mDeps.isAtLeastS()) {
-            pw.println("No counter support");
-            return;
-        }
-        try (IBpfMap<S32, S32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
-                S32.class, S32.class)) {
-
+        try (IBpfMap<S32, S32> map = mDeps.getBpfErrorMap()) {
+            if (map == null) {
+                pw.println("No error counter support");
+                return;
+            }
+            if (map.isEmpty()) {
+                pw.println("<empty>");
+                return;
+            }
             map.forEach((k, v) -> {
                 String counterName;
                 try {
@@ -1300,7 +1321,7 @@
                 if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
             });
         } catch (ErrnoException | IOException e) {
-            pw.println("Error dumping counter map: " + e);
+            pw.println("Error dumping error counter map: " + e);
         }
     }
 
@@ -1466,6 +1487,15 @@
             // to Objects.hash() to avoid autoboxing overhead.
             return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac);
         }
+
+        @Override
+        public String toString() {
+            return "upstreamIfindex: " + upstreamIfindex
+                    + ", downstreamIfindex: " + downstreamIfindex
+                    + ", address: " + address.getHostAddress()
+                    + ", srcMac: " + srcMac
+                    + ", dstMac: " + dstMac;
+        }
     }
 
     /** Tethering client information class. */
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 784ebd5..6d502ce 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -34,6 +34,7 @@
 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 
 import static com.android.networkstack.apishim.ConstantsShim.ACTION_TETHER_UNSUPPORTED_CARRIER_UI;
+import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_NOT_EXPORTED;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -119,8 +120,13 @@
         mEntitlementCacheValue = new SparseIntArray();
         mPermissionChangeCallback = callback;
         mHandler = h;
-        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
-                null, mHandler);
+        if (SdkLevel.isAtLeastU()) {
+            mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
+                    null, mHandler, RECEIVER_NOT_EXPORTED);
+        } else {
+            mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
+                    null, mHandler);
+        }
         mSilentProvisioningService = ComponentName.unflattenFromString(
                 mContext.getResources().getString(R.string.config_wifi_tether_enable));
     }
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index fbb342d..846abcb 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -295,8 +295,7 @@
                 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
         if (h1 == null) return false;
 
-        sendIpv4NfGenMsg(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
-                           (short) (NLM_F_REQUEST | NLM_F_DUMP));
+        requestSocketDump(h1);
 
         final NativeHandle h2 = mDeps.createConntrackSocket(
                 NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
@@ -325,7 +324,7 @@
     }
 
     @VisibleForTesting
-    public void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
+    void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
         final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
         final byte[] msg = new byte[length];
         final ByteBuffer byteBuffer = ByteBuffer.wrap(msg);
@@ -350,6 +349,12 @@
         }
     }
 
+    @VisibleForTesting
+    void requestSocketDump(NativeHandle handle) {
+        sendIpv4NfGenMsg(handle, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
+                (short) (NLM_F_REQUEST | NLM_F_DUMP));
+    }
+
     private void closeFdInNativeHandle(final NativeHandle h) {
         try {
             h.close();
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java b/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java
index a7e8ccf..68d694a 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java
@@ -28,26 +28,4 @@
     public TetherLimitKey(final int ifindex) {
         this.ifindex = ifindex;
     }
-
-    // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof TetherLimitKey)) return false;
-
-        final TetherLimitKey that = (TetherLimitKey) obj;
-
-        return ifindex == that.ifindex;
-    }
-
-    @Override
-    public int hashCode() {
-        return Integer.hashCode(ifindex);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ifindex: %d", ifindex);
-    }
 }
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherLimitValue.java b/Tethering/src/com/android/networkstack/tethering/TetherLimitValue.java
index ed7e7d4..00dfcc6 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetherLimitValue.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetherLimitValue.java
@@ -32,26 +32,4 @@
     public TetherLimitValue(final long limit) {
         this.limit = limit;
     }
-
-    // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof TetherLimitValue)) return false;
-
-        final TetherLimitValue that = (TetherLimitValue) obj;
-
-        return limit == that.limit;
-    }
-
-    @Override
-    public int hashCode() {
-        return Long.hashCode(limit);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("limit: %d", limit);
-    }
 }
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0d1b22e..f0dd030 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -477,17 +477,10 @@
             wifiManager.registerSoftApCallback(mExecutor, softApCallback);
         }
         if (SdkLevel.isAtLeastT() && wifiManager != null) {
-            try {
-                // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
-                // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
-                // or MAINLINE_NETWORK_STACK permission would also able to use this API.
-                wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor, softApCallback);
-            } catch (UnsupportedOperationException e) {
-                // Since wifi module development in internal branch,
-                // #registerLocalOnlyHotspotSoftApCallback currently doesn't supported in AOSP
-                // before AOSP switch to Android T + 1.
-                Log.wtf(TAG, "registerLocalOnlyHotspotSoftApCallback API is not supported");
-            }
+            // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
+            // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
+            // or MAINLINE_NETWORK_STACK permission would also able to use this API.
+            wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor, softApCallback);
         }
 
         startTrackDefaultNetwork();
@@ -1008,8 +1001,7 @@
         if (request != null) {
             mActiveTetheringRequests.delete(type);
         }
-        tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
-                request);
+        tetherState.ipServer.enable(requestedState, request);
         return TETHER_ERROR_NO_ERROR;
     }
 
@@ -1033,7 +1025,7 @@
             Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
             return TETHER_ERROR_UNAVAIL_IFACE;
         }
-        tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
+        tetherState.ipServer.unwanted();
         return TETHER_ERROR_NO_ERROR;
     }
 
@@ -1094,8 +1086,6 @@
         final ArrayList<TetheringInterface> errored = new ArrayList<>();
         final ArrayList<Integer> lastErrors = new ArrayList<>();
 
-        final TetheringConfiguration cfg = mConfig;
-
         int downstreamTypesMask = DOWNSTREAM_NONE;
         for (int i = 0; i < mTetherStates.size(); i++) {
             final TetherState tetherState = mTetherStates.valueAt(i);
@@ -2004,10 +1994,6 @@
                 return;
             }
 
-            if (arg1 == UpstreamNetworkMonitor.NOTIFY_TEST_NETWORK_AVAILABLE) {
-                chooseUpstreamType(false);
-            }
-
             if (ns == null || !pertainsToCurrentUpstream(ns)) {
                 // TODO: In future, this is where upstream evaluation and selection
                 // could be handled for notifications which include sufficient data.
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 696a970..903de9d 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -22,6 +22,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
 
 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
 import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
@@ -193,8 +194,13 @@
 
         isDunRequired = checkDunRequired(ctx);
 
-        final boolean forceAutomaticUpstream = !SdkLevel.isAtLeastS()
-                && isFeatureEnabled(ctx, TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION);
+        // Here is how automatic mode enable/disable support on different Android version:
+        // - R   : can be enabled/disabled by resource config_tether_upstream_automatic.
+        //         but can be force-enabled by flag TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION.
+        // - S, T: can be enabled/disabled by resource config_tether_upstream_automatic.
+        // - U+  : automatic mode only.
+        final boolean forceAutomaticUpstream = SdkLevel.isAtLeastU() || (!SdkLevel.isAtLeastS()
+                && isConnectivityFeatureEnabled(ctx, TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION));
         chooseUpstreamAutomatically = forceAutomaticUpstream || getResourceBoolean(
                 res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
@@ -565,9 +571,23 @@
         return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
     }
 
+    /**
+     * This is deprecated because connectivity namespace already be used for NetworkStack mainline
+     * module. Tethering should use its own namespace to roll out the feature flag.
+     * @deprecated new caller should use isTetheringFeatureEnabled instead.
+     */
+    @Deprecated
+    private boolean isConnectivityFeatureEnabled(Context ctx, String featureVersionFlag) {
+        return isFeatureEnabled(ctx, NAMESPACE_CONNECTIVITY, featureVersionFlag);
+    }
+
+    private boolean isTetheringFeatureEnabled(Context ctx, String featureVersionFlag) {
+        return isFeatureEnabled(ctx, NAMESPACE_TETHERING, featureVersionFlag);
+    }
+
     @VisibleForTesting
-    protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
-        return DeviceConfigUtils.isFeatureEnabled(ctx, NAMESPACE_CONNECTIVITY, featureVersionFlag,
+    protected boolean isFeatureEnabled(Context ctx, String namespace, String featureVersionFlag) {
+        return DeviceConfigUtils.isFeatureEnabled(ctx, namespace, featureVersionFlag,
                 TETHERING_MODULE_NAME, false /* defaultEnabled */);
     }
 
@@ -652,21 +672,13 @@
      */
     public TetheringConfigurationParcel toStableParcelable() {
         final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
-        parcel.subId = activeDataSubId;
         parcel.tetherableUsbRegexs = tetherableUsbRegexs;
         parcel.tetherableWifiRegexs = tetherableWifiRegexs;
         parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
-        parcel.isDunRequired = isDunRequired;
-        parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
-
-        parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes);
-
         parcel.legacyDhcpRanges = legacyDhcpRanges;
-        parcel.defaultIPv4DNS = defaultIPv4DNS;
-        parcel.enableLegacyDhcpServer = mEnableLegacyDhcpServer;
         parcel.provisioningApp = provisioningApp;
         parcel.provisioningAppNoUi = provisioningAppNoUi;
-        parcel.provisioningCheckPeriod = provisioningCheckPeriod;
+
         return parcel;
     }
 }
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 15df0c6..16c031b 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -85,12 +85,11 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
 
-    public static final int EVENT_ON_CAPABILITIES         = 1;
-    public static final int EVENT_ON_LINKPROPERTIES       = 2;
-    public static final int EVENT_ON_LOST                 = 3;
-    public static final int EVENT_DEFAULT_SWITCHED        = 4;
-    public static final int NOTIFY_LOCAL_PREFIXES         = 10;
-    public static final int NOTIFY_TEST_NETWORK_AVAILABLE = 11;
+    public static final int EVENT_ON_CAPABILITIES   = 1;
+    public static final int EVENT_ON_LINKPROPERTIES = 2;
+    public static final int EVENT_ON_LOST           = 3;
+    public static final int EVENT_DEFAULT_SWITCHED  = 4;
+    public static final int NOTIFY_LOCAL_PREFIXES   = 10;
     // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
     // disabled.
     @VisibleForTesting
@@ -468,17 +467,6 @@
         notifyTarget(EVENT_DEFAULT_SWITCHED, ns);
     }
 
-    private void maybeHandleTestNetwork(@NonNull Network network) {
-        if (!mPreferTestNetworks) return;
-
-        final UpstreamNetworkState ns = mNetworkMap.get(network);
-        if (network.equals(mTetheringUpstreamNetwork) || !isTestNetwork(ns)) return;
-
-        // Test network is available. Notify tethering.
-        Log.d(TAG, "Handle test network: " + network);
-        notifyTarget(NOTIFY_TEST_NETWORK_AVAILABLE, ns);
-    }
-
     private void recomputeLocalPrefixes() {
         final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
         if (!mLocalPrefixes.equals(localPrefixes)) {
@@ -561,12 +549,6 @@
             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
             if (mCallbackType == CALLBACK_LISTEN_ALL) {
                 recomputeLocalPrefixes();
-
-                // When the LISTEN_ALL network callback calls onLinkPropertiesChanged, it means that
-                // all the network information for the network is known (because
-                // onLinkPropertiesChanged is called after onAvailable and onCapabilitiesChanged).
-                // Inform tethering that the test network might have changed.
-                maybeHandleTestNetwork(network);
             }
         }
 
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index d8e631e..ffcea4e 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -75,7 +75,8 @@
                     .setUserType(userTypeToEnum(callerPkg))
                     .setUpstreamType(UpstreamType.UT_UNKNOWN)
                     .setErrorCode(ErrorCode.EC_NO_ERROR)
-                    .build();
+                    .setUpstreamEvents(UpstreamEvents.newBuilder())
+                    .setDurationMillis(0);
         mBuilderMap.put(downstreamType, statsBuilder);
     }
 
@@ -110,7 +111,8 @@
                 reported.getErrorCode().getNumber(),
                 reported.getDownstreamType().getNumber(),
                 reported.getUpstreamType().getNumber(),
-                reported.getUserType().getNumber());
+                reported.getUserType().getNumber(),
+                null, 0);
         if (DBG) {
             Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
                     + ", downstreamType: " + reported.getDownstreamType().getNumber()
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
index 46a47af..27f2126 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
@@ -21,12 +21,38 @@
 
 import "frameworks/proto_logging/stats/enums/stats/connectivity/tethering.proto";
 
+// Logs each upstream for a successful switch over
+message UpstreamEvent {
+  // Transport type of upstream network
+  optional .android.stats.connectivity.UpstreamType upstream_type = 1;
+
+  // A time period that an upstream continued
+  optional int64 duration_millis = 2;
+}
+
+message UpstreamEvents {
+  repeated UpstreamEvent upstream_event = 1;
+}
+
 /**
  * Logs Tethering events
  */
 message NetworkTetheringReported {
-   optional .android.stats.connectivity.ErrorCode error_code = 1;
-   optional .android.stats.connectivity.DownstreamType downstream_type = 2;
-   optional .android.stats.connectivity.UpstreamType upstream_type = 3;
-   optional .android.stats.connectivity.UserType user_type = 4;
+    // Tethering error code
+    optional .android.stats.connectivity.ErrorCode error_code = 1;
+
+    // Tethering downstream type
+    optional .android.stats.connectivity.DownstreamType downstream_type = 2;
+
+    // Transport type of upstream network
+    optional .android.stats.connectivity.UpstreamType upstream_type = 3  [deprecated = true];
+
+    // The user type of switching tethering
+    optional .android.stats.connectivity.UserType user_type = 4;
+
+    // Log each transport type of upstream network event
+    optional UpstreamEvents upstream_events = 5;
+
+    // A time period that a downstreams exists
+    optional int64 duration_millis = 6;
 }
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index 9aa2cff..11e3dc0 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -28,6 +28,7 @@
     static_libs: [
         "NetworkStackApiStableLib",
         "androidx.test.rules",
+        "cts-net-utils",
         "mockito-target-extended-minus-junit4",
         "net-tests-utils",
         "net-utils-device-common-bpf",
diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml
index 9303d0a..7527913 100644
--- a/Tethering/tests/integration/AndroidManifest.xml
+++ b/Tethering/tests/integration/AndroidManifest.xml
@@ -17,6 +17,7 @@
           package="com.android.networkstack.tethering.tests.integration">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
          network. Since R shell application don't have such permission, grant permission to the test
          here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to
diff --git a/Tethering/tests/integration/src/android/net/CtsEthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/CtsEthernetTetheringTest.java
new file mode 100644
index 0000000..aea6728
--- /dev/null
+++ b/Tethering/tests/integration/src/android/net/CtsEthernetTetheringTest.java
@@ -0,0 +1,1076 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import static android.Manifest.permission.DUMP;
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringTester.TestDnsPacket;
+import static android.net.TetheringTester.isExpectedIcmpPacket;
+import static android.net.TetheringTester.isExpectedUdpDnsPacket;
+import static android.system.OsConstants.ICMP_ECHO;
+import static android.system.OsConstants.ICMP_ECHOREPLY;
+import static android.system.OsConstants.IPPROTO_ICMP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
+import static com.android.net.module.util.HexDump.dumpHexString;
+import static com.android.net.module.util.IpUtils.icmpChecksum;
+import static com.android.net.module.util.IpUtils.ipChecksum;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static com.android.net.module.util.NetworkStackConstants.ICMP_CHECKSUM_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
+import static com.android.testutils.DeviceInfoUtils.KVersion;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.net.TetheringManager.TetheringRequest;
+import android.net.TetheringTester.TetheredDevice;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.BpfDump;
+import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.bpf.Tether4Key;
+import com.android.net.module.util.bpf.Tether4Value;
+import com.android.net.module.util.bpf.TetherStatsKey;
+import com.android.net.module.util.bpf.TetherStatsValue;
+import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv4Header;
+import com.android.net.module.util.structs.Ipv4Header;
+import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.UdpHeader;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DeviceInfoUtils;
+import com.android.testutils.DumpTestUtils;
+import com.android.testutils.TapPacketReader;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class CtsEthernetTetheringTest extends EthernetTetheringTestBase {
+    @Rule
+    public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
+    private static final String TAG = CtsEthernetTetheringTest.class.getSimpleName();
+
+    private static final int DUMP_POLLING_MAX_RETRY = 100;
+    private static final int DUMP_POLLING_INTERVAL_MS = 50;
+    // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
+    // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
+    private static final int UDP_STREAM_TS_MS = 2000;
+    // Give slack time for waiting UDP stream mode because handling conntrack event in user space
+    // may not in precise time. Used to reduce the flaky rate.
+    private static final int UDP_STREAM_SLACK_MS = 500;
+    // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+    private static final int RX_UDP_PACKET_SIZE = 30;
+    private static final int RX_UDP_PACKET_COUNT = 456;
+    // Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes.
+    private static final int TX_UDP_PACKET_SIZE = 44;
+    private static final int TX_UDP_PACKET_COUNT = 123;
+
+    private static final short DNS_PORT = 53;
+
+    private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
+    private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
+    private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
+    private static final String LINE_DELIMITER = "\\n";
+
+    private static final short ICMPECHO_CODE = 0x0;
+    private static final short ICMPECHO_ID = 0x0;
+    private static final short ICMPECHO_SEQ = 0x0;
+
+    // TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports
+    // building packet for given arguments.
+    private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] {
+            // scapy.DNS(
+            //   id=0xbeef,
+            //   qr=0,
+            //   qd=scapy.DNSQR(qname="hello.example.com"))
+            //
+            /* Header */
+            (byte) 0xbe, (byte) 0xef, /* Transaction ID: 0xbeef */
+            (byte) 0x01, (byte) 0x00, /* Flags: rd */
+            (byte) 0x00, (byte) 0x01, /* Questions: 1 */
+            (byte) 0x00, (byte) 0x00, /* Answer RRs: 0 */
+            (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
+            (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
+            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
+            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
+            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
+            (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */
+            (byte) 0x00, (byte) 0x01,              /* Type: A */
+            (byte) 0x00, (byte) 0x01               /* Class: IN */
+    });
+
+    private static final byte[] DNS_REPLY = new byte[] {
+            // scapy.DNS(
+            //   id=0,
+            //   qr=1,
+            //   qd=scapy.DNSQR(qname="hello.example.com"),
+            //   an=scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4'))
+            //
+            /* Header */
+            (byte) 0x00, (byte) 0x00, /* Transaction ID: 0x0, must be updated by dns query id */
+            (byte) 0x81, (byte) 0x00, /* Flags: qr rd */
+            (byte) 0x00, (byte) 0x01, /* Questions: 1 */
+            (byte) 0x00, (byte) 0x01, /* Answer RRs: 1 */
+            (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
+            (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
+            /* Queries */
+            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
+            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
+            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
+            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
+            (byte) 0x6f, (byte) 0x6d, (byte) 0x00,              /* Name: hello.example.com */
+            (byte) 0x00, (byte) 0x01,                           /* Type: A */
+            (byte) 0x00, (byte) 0x01,                           /* Class: IN */
+            /* Answers */
+            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
+            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
+            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
+            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
+            (byte) 0x6f, (byte) 0x6d, (byte) 0x00,              /* Name: hello.example.com */
+            (byte) 0x00, (byte) 0x01,                           /* Type: A */
+            (byte) 0x00, (byte) 0x01,                           /* Class: IN */
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* Time to live: 0 */
+            (byte) 0x00, (byte) 0x04,                           /* Data length: 4 */
+            (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04  /* Address: 1.2.3.4 */
+    };
+
+    @Test
+    public void testVirtualEthernetAlreadyExists() throws Exception {
+        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
+        assumeFalse(isInterfaceForTetheringAvailable());
+
+        TestNetworkInterface downstreamIface = null;
+        MyTetheringEventCallback tetheringEventCallback = null;
+        TapPacketReader downstreamReader = null;
+
+        try {
+            downstreamIface = createTestInterface();
+            // This must be done now because as soon as setIncludeTestInterfaces(true) is called,
+            // the interface will be placed in client mode, which will delete the link-local
+            // address. At that point NetworkInterface.getByName() will cease to work on the
+            // interface, because starting in R NetworkInterface can no longer see interfaces
+            // without IP addresses.
+            int mtu = getMTU(downstreamIface);
+
+            Log.d(TAG, "Including test interfaces");
+            setIncludeTestInterfaces(true);
+
+            final String iface = getTetheredInterface();
+            assertEquals("TetheredInterfaceCallback for unexpected interface",
+                    downstreamIface.getInterfaceName(), iface);
+
+            // Check virtual ethernet.
+            FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor();
+            downstreamReader = makePacketReader(fd, mtu);
+            tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(),
+                    null /* any upstream */);
+            checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback);
+        } finally {
+            maybeStopTapPacketReader(downstreamReader);
+            maybeCloseTestInterface(downstreamIface);
+            maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+        }
+    }
+
+    @Test
+    public void testVirtualEthernet() throws Exception {
+        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
+        assumeFalse(isInterfaceForTetheringAvailable());
+
+        CompletableFuture<String> futureIface = requestTetheredInterface();
+
+        setIncludeTestInterfaces(true);
+
+        TestNetworkInterface downstreamIface = null;
+        MyTetheringEventCallback tetheringEventCallback = null;
+        TapPacketReader downstreamReader = null;
+
+        try {
+            downstreamIface = createTestInterface();
+
+            final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            assertEquals("TetheredInterfaceCallback for unexpected interface",
+                    downstreamIface.getInterfaceName(), iface);
+
+            // Check virtual ethernet.
+            FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor();
+            downstreamReader = makePacketReader(fd, getMTU(downstreamIface));
+            tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(),
+                    null /* any upstream */);
+            checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback);
+        } finally {
+            maybeStopTapPacketReader(downstreamReader);
+            maybeCloseTestInterface(downstreamIface);
+            maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+        }
+    }
+
+    @Test
+    public void testStaticIpv4() throws Exception {
+        assumeFalse(isInterfaceForTetheringAvailable());
+
+        setIncludeTestInterfaces(true);
+
+        TestNetworkInterface downstreamIface = null;
+        MyTetheringEventCallback tetheringEventCallback = null;
+        TapPacketReader downstreamReader = null;
+
+        try {
+            downstreamIface = createTestInterface();
+
+            final String iface = getTetheredInterface();
+            assertEquals("TetheredInterfaceCallback for unexpected interface",
+                    downstreamIface.getInterfaceName(), iface);
+
+            assertInvalidStaticIpv4Request(iface, null, null);
+            assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
+            assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
+            assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
+            assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
+            assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
+            assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
+
+            final String localAddr = "192.0.2.3/28";
+            final String clientAddr = "192.0.2.2/28";
+            tetheringEventCallback = enableEthernetTethering(iface,
+                    requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */);
+
+            tetheringEventCallback.awaitInterfaceTethered();
+            assertInterfaceHasIpAddress(iface, localAddr);
+
+            byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
+            byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
+
+            FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor();
+            downstreamReader = makePacketReader(fd, getMTU(downstreamIface));
+            TetheringTester tester = new TetheringTester(downstreamReader);
+            DhcpResults dhcpResults = tester.runDhcp(client1);
+            assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
+
+            try {
+                tester.runDhcp(client2);
+                fail("Only one client should get an IP address");
+            } catch (TimeoutException expected) { }
+        } finally {
+            maybeStopTapPacketReader(downstreamReader);
+            maybeCloseTestInterface(downstreamIface);
+            maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+        }
+    }
+
+    private static void expectLocalOnlyAddresses(String iface) throws Exception {
+        final List<InterfaceAddress> interfaceAddresses =
+                NetworkInterface.getByName(iface).getInterfaceAddresses();
+
+        boolean foundIpv6Ula = false;
+        for (InterfaceAddress ia : interfaceAddresses) {
+            final InetAddress addr = ia.getAddress();
+            if (isIPv6ULA(addr)) {
+                foundIpv6Ula = true;
+            }
+            final int prefixlen = ia.getNetworkPrefixLength();
+            final LinkAddress la = new LinkAddress(addr, prefixlen);
+            if (la.isIpv6() && la.isGlobalPreferred()) {
+                fail("Found global IPv6 address on local-only interface: " + interfaceAddresses);
+            }
+        }
+
+        assertTrue("Did not find IPv6 ULA on local-only interface " + iface,
+                foundIpv6Ula);
+    }
+
+    @Test
+    public void testLocalOnlyTethering() throws Exception {
+        assumeFalse(isInterfaceForTetheringAvailable());
+
+        setIncludeTestInterfaces(true);
+
+        TestNetworkInterface downstreamIface = null;
+        MyTetheringEventCallback tetheringEventCallback = null;
+        TapPacketReader downstreamReader = null;
+
+        try {
+            downstreamIface = createTestInterface();
+
+            final String iface = getTetheredInterface();
+            assertEquals("TetheredInterfaceCallback for unexpected interface",
+                    downstreamIface.getInterfaceName(), iface);
+
+            final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET)
+                    .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build();
+            tetheringEventCallback = enableEthernetTethering(iface, request,
+                    null /* any upstream */);
+            tetheringEventCallback.awaitInterfaceLocalOnly();
+
+            // makePacketReader only works after tethering is started, because until then the
+            // interface does not have an IP address, and unprivileged apps cannot see interfaces
+            // without IP addresses. This shouldn't be flaky because the TAP interface will buffer
+            // all packets even before the reader is started.
+            downstreamReader = makePacketReader(downstreamIface);
+
+            waitForRouterAdvertisement(downstreamReader, iface, WAIT_RA_TIMEOUT_MS);
+            expectLocalOnlyAddresses(iface);
+        } finally {
+            maybeStopTapPacketReader(downstreamReader);
+            maybeCloseTestInterface(downstreamIface);
+            maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+        }
+    }
+
+    private boolean isAdbOverNetwork() {
+        // If adb TCP port opened, this test may running by adb over network.
+        return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+                || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+    }
+
+    @Test
+    public void testPhysicalEthernet() throws Exception {
+        assumeTrue(isInterfaceForTetheringAvailable());
+        // Do not run this test if adb is over network and ethernet is connected.
+        // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+        // from client mode to server mode. See b/160389275.
+        assumeFalse(isAdbOverNetwork());
+
+        MyTetheringEventCallback tetheringEventCallback = null;
+        try {
+            // Get an interface to use.
+            final String iface = getTetheredInterface();
+
+            // Enable Ethernet tethering and check that it starts.
+            tetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */);
+        } finally {
+            maybeUnregisterTetheringEventCallback(tetheringEventCallback);
+        }
+        // There is nothing more we can do on a physical interface without connecting an actual
+        // client, which is not possible in this test.
+    }
+
+    private void checkTetheredClientCallbacks(final TapPacketReader packetReader,
+            final MyTetheringEventCallback tetheringEventCallback) throws Exception {
+        // Create a fake client.
+        byte[] clientMacAddr = new byte[6];
+        new Random().nextBytes(clientMacAddr);
+
+        TetheringTester tester = new TetheringTester(packetReader);
+        DhcpResults dhcpResults = tester.runDhcp(clientMacAddr);
+
+        final Collection<TetheredClient> clients = tetheringEventCallback.awaitClientConnected();
+        assertEquals(1, clients.size());
+        final TetheredClient client = clients.iterator().next();
+
+        // Check the MAC address.
+        assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
+        assertEquals(TETHERING_ETHERNET, client.getTetheringType());
+
+        // Check the hostname.
+        assertEquals(1, client.getAddresses().size());
+        TetheredClient.AddressInfo info = client.getAddresses().get(0);
+        assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname());
+
+        // Check the address is the one that was handed out in the DHCP ACK.
+        assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
+
+        // Check that the lifetime is correct +/- 10s.
+        final long now = SystemClock.elapsedRealtime();
+        final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000;
+        final String msg = String.format("IP address should have lifetime of %d, got %d",
+                dhcpResults.leaseDuration, actualLeaseDuration);
+        assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
+    }
+
+    public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
+        // Check all fields except the deprecation and expiry times.
+        String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
+        assertTrue(msg, l1.isSameAddressAs(l2));
+        assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags());
+        assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
+    }
+
+    private TetheringRequest requestWithStaticIpv4(String local, String client) {
+        LinkAddress localAddr = local == null ? null : new LinkAddress(local);
+        LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
+        return new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setStaticIpv4Addresses(localAddr, clientAddr)
+                .setShouldShowEntitlementUi(false).build();
+    }
+
+    private void assertInvalidStaticIpv4Request(String iface, String local, String client)
+            throws Exception {
+        try {
+            enableEthernetTethering(iface, requestWithStaticIpv4(local, client),
+                    null /* any upstream */);
+            fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
+        } catch (IllegalArgumentException | NullPointerException expected) { }
+    }
+
+    private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
+        LinkAddress expectedAddr = new LinkAddress(expected);
+        NetworkInterface nif = NetworkInterface.getByName(iface);
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (expectedAddr.equals(addr)) {
+                return;
+            }
+        }
+        fail("Expected " + iface + " to have IP address " + expected + ", found "
+                + nif.getInterfaceAddresses());
+    }
+
+    @Test
+    public void testIcmpv6Echo() throws Exception {
+        runPing6Test(initTetheringTester(toList(TEST_IP4_ADDR, TEST_IP6_ADDR),
+                toList(TEST_IP4_DNS, TEST_IP6_DNS)));
+    }
+
+    private void runPing6Test(TetheringTester tester) throws Exception {
+        TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+        Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222");
+        ByteBuffer request = Ipv6Utils.buildEchoRequestPacket(tethered.macAddr,
+                tethered.routerMacAddr, tethered.ipv6Addr, remoteIp6Addr);
+        tester.verifyUpload(request, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */,
+                    ICMPV6_ECHO_REQUEST_TYPE);
+        });
+
+        ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(remoteIp6Addr, tethered.ipv6Addr);
+        tester.verifyDownload(reply, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */,
+                    ICMPV6_ECHO_REPLY_TYPE);
+        });
+    }
+
+    @Test
+    public void testTetherUdpV6() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr,
+                tethered.ipv6Addr, REMOTE_IP6_ADDR, tester, false /* is4To6 */);
+        sendDownloadPacketUdp(REMOTE_IP6_ADDR, tethered.ipv6Addr, tester, false /* is6To4 */);
+
+        // TODO: test BPF offload maps {rule, stats}.
+    }
+
+    // Test network topology:
+    //
+    //         public network (rawip)                 private network
+    //                   |                 UE                |
+    // +------------+    V    +------------+------------+    V    +------------+
+    // |   Sever    +---------+  Upstream  | Downstream +---------+   Client   |
+    // +------------+         +------------+------------+         +------------+
+    // remote ip              public ip                           private ip
+    // 8.8.8.8:443            <Upstream ip>:9876                  <TetheredDevice ip>:9876
+    //
+    private void runUdp4Test(boolean verifyBpf) throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+        // TODO: remove the connectivity verification for upstream connected notification race.
+        // Because async upstream connected notification can't guarantee the tethering routing is
+        // ready to use. Need to test tethering connectivity before testing.
+        // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
+        // from upstream. That can guarantee that the routing is ready. Long term plan is that
+        // refactors upstream connected notification from async to sync.
+        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+        final MacAddress srcMac = tethered.macAddr;
+        final MacAddress dstMac = tethered.routerMacAddr;
+        final InetAddress remoteIp = REMOTE_IP4_ADDR;
+        final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
+        final InetAddress clientIp = tethered.ipv4Addr;
+        sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+        sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+
+        if (verifyBpf) {
+            // Send second UDP packet in original direction.
+            // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
+            // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
+            // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
+            // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
+            // and apply ASSURED flag.
+            // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
+            // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
+            Thread.sleep(UDP_STREAM_TS_MS);
+            sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
+
+            // Give a slack time for handling conntrack event in user space.
+            Thread.sleep(UDP_STREAM_SLACK_MS);
+
+            // [1] Verify IPv4 upstream rule map.
+            final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
+                    Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
+            assertNotNull(upstreamMap);
+            assertEquals(1, upstreamMap.size());
+
+            final Map.Entry<Tether4Key, Tether4Value> rule =
+                    upstreamMap.entrySet().iterator().next();
+
+            final Tether4Key upstream4Key = rule.getKey();
+            assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
+            assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
+            assertEquals(LOCAL_PORT, upstream4Key.srcPort);
+            assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
+            assertEquals(REMOTE_PORT, upstream4Key.dstPort);
+
+            final Tether4Value upstream4Value = rule.getValue();
+            assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
+                    InetAddress.getByAddress(upstream4Value.src46).getAddress()));
+            assertEquals(LOCAL_PORT, upstream4Value.srcPort);
+            assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
+                    InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
+            assertEquals(REMOTE_PORT, upstream4Value.dstPort);
+
+            // [2] Verify stats map.
+            // Transmit packets on both direction for verifying stats. Because we only care the
+            // packet count in stats test, we just reuse the existing packets to increaes
+            // the packet count on both direction.
+
+            // Send packets on original direction.
+            for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
+                sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
+                        false /* is4To6 */);
+            }
+
+            // Send packets on reply direction.
+            for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
+                sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
+            }
+
+            // Dump stats map to verify.
+            final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
+                    TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
+            assertNotNull(statsMap);
+            assertEquals(1, statsMap.size());
+
+            final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
+                    statsMap.entrySet().iterator().next();
+
+            // TODO: verify the upstream index in TetherStatsKey.
+
+            final TetherStatsValue statsValue = stats.getValue();
+            assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
+            assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
+            assertEquals(0, statsValue.rxErrors);
+            assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
+            assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
+            assertEquals(0, statsValue.txErrors);
+        }
+    }
+
+    private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
+        final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
+        return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
+                || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
+                || current.isAtLeast(new KVersion(5, 4, 98));
+    }
+
+    @Test
+    public void testIsUdpOffloadSupportedByKernel() throws Exception {
+        assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
+        assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
+
+        assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
+        assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
+        assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
+
+        assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
+        assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
+    }
+
+    private static void assumeKernelSupportBpfOffloadUdpV4() {
+        final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
+        assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
+                isUdpOffloadSupportedByKernel(kernelVersion));
+    }
+
+    @Test
+    public void testKernelSupportBpfOffloadUdpV4() throws Exception {
+        assumeKernelSupportBpfOffloadUdpV4();
+    }
+
+    @Test
+    public void testTetherConfigBpfOffloadEnabled() throws Exception {
+        assumeTrue(isTetherConfigBpfOffloadEnabled());
+    }
+
+    /**
+     * Basic IPv4 UDP tethering test. Verify that UDP tethered packets are transferred no matter
+     * using which data path.
+     */
+    @Test
+    public void testTetherUdpV4() throws Exception {
+        runUdp4Test(false /* verifyBpf */);
+    }
+
+    /**
+     * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
+     * Minimum test requirement:
+     * 1. S+ device.
+     * 2. Tethering config enables tethering BPF offload.
+     * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
+     *
+     * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
+     */
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testTetherUdpV4_VerifyBpf() throws Exception {
+        assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
+        assumeKernelSupportBpfOffloadUdpV4();
+
+        runUdp4Test(true /* verifyBpf */);
+    }
+
+    @NonNull
+    private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
+            Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+            throws Exception {
+        final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
+        final String rawMapStr = runAsShell(DUMP, () ->
+                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
+        final HashMap<K, V> map = new HashMap<>();
+
+        for (final String line : rawMapStr.split(LINE_DELIMITER)) {
+            final Pair<K, V> rule =
+                    BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
+            map.put(rule.first, rule.second);
+        }
+        return map;
+    }
+
+    @Nullable
+    private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
+            Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+            throws Exception {
+        for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
+            final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
+            if (!map.isEmpty()) return map;
+
+            Thread.sleep(DUMP_POLLING_INTERVAL_MS);
+        }
+
+        fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
+        return null;
+    }
+
+    private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
+        final String dumpStr = runAsShell(DUMP, () ->
+                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
+
+        // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
+        // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
+        // RRO to override the enabled default value. Get the tethering config via dumpsys.
+        // $ dumpsys tethering
+        //   mIsBpfEnabled: true
+        boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
+        if (!enabled) {
+            Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
+        }
+        return enabled;
+    }
+
+    @NonNull
+    private Inet6Address getClatIpv6Address(TetheringTester tester, TetheredDevice tethered)
+            throws Exception {
+        // Send an IPv4 UDP packet from client and check that a CLAT translated IPv6 UDP packet can
+        // be found on upstream interface. Get CLAT IPv6 address from the CLAT translated IPv6 UDP
+        // packet.
+        byte[] expectedPacket = probeV4TetheringConnectivity(tester, tethered, true /* is4To6 */);
+
+        // Above has guaranteed that the found packet is an IPv6 packet without ether header.
+        return Struct.parse(Ipv6Header.class, ByteBuffer.wrap(expectedPacket)).srcIp;
+    }
+
+    // Test network topology:
+    //
+    //            public network (rawip)                 private network
+    //                      |         UE (CLAT support)         |
+    // +---------------+    V    +------------+------------+    V    +------------+
+    // | NAT64 Gateway +---------+  Upstream  | Downstream +---------+   Client   |
+    // +---------------+         +------------+------------+         +------------+
+    // remote ip                 public ip                           private ip
+    // [64:ff9b::808:808]:443    [clat ipv6]:9876                    [TetheredDevice ipv4]:9876
+    //
+    // Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by
+    // sending out an IPv4 packet and extracting the source address from CLAT translated IPv6
+    // packet.
+    //
+    private void runClatUdpTest() throws Exception {
+        // CLAT only starts on IPv6 only network.
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+
+        // Get CLAT IPv6 address.
+        final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered);
+
+        // Send an IPv4 UDP packet in original direction.
+        // IPv4 packet -- CLAT translation --> IPv6 packet
+        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr,
+                REMOTE_IP4_ADDR, tester, true /* is4To6 */);
+
+        // Send an IPv6 UDP packet in reply direction.
+        // IPv6 packet -- CLAT translation --> IPv4 packet
+        sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */);
+
+        // TODO: test CLAT bpf maps.
+    }
+
+    // TODO: support R device. See b/234727688.
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testTetherClatUdp() throws Exception {
+        runClatUdpTest();
+    }
+
+    // PacketBuilder doesn't support IPv4 ICMP packet. It may need to refactor PacketBuilder first
+    // because ICMP is a specific layer 3 protocol for PacketBuilder which expects packets always
+    // have layer 3 (IP) and layer 4 (TCP, UDP) for now. Since we don't use IPv4 ICMP packet too
+    // much in this test, we just write a ICMP packet builder here.
+    // TODO: move ICMPv4 packet build function to common utilis.
+    @NonNull
+    private ByteBuffer buildIcmpEchoPacketV4(
+            @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+            @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp,
+            int type, short id, short seq) throws Exception {
+        if (type != ICMP_ECHO && type != ICMP_ECHOREPLY) {
+            fail("Unsupported ICMP type: " + type);
+        }
+
+        // Build ICMP echo id and seq fields as payload. Ignore the data field.
+        final ByteBuffer payload = ByteBuffer.allocate(4);
+        payload.putShort(id);
+        payload.putShort(seq);
+        payload.rewind();
+
+        final boolean hasEther = (srcMac != null && dstMac != null);
+        final int etherHeaderLen = hasEther ? Struct.getSize(EthernetHeader.class) : 0;
+        final int ipv4HeaderLen = Struct.getSize(Ipv4Header.class);
+        final int Icmpv4HeaderLen = Struct.getSize(Icmpv4Header.class);
+        final int payloadLen = payload.limit();
+        final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv4HeaderLen
+                + Icmpv4HeaderLen + payloadLen);
+
+        // [1] Ethernet header
+        if (hasEther) {
+            final EthernetHeader ethHeader = new EthernetHeader(dstMac, srcMac, ETHER_TYPE_IPV4);
+            ethHeader.writeToByteBuffer(packet);
+        }
+
+        // [2] IP header
+        final Ipv4Header ipv4Header = new Ipv4Header(TYPE_OF_SERVICE,
+                (short) 0 /* totalLength, calculate later */, ID,
+                FLAGS_AND_FRAGMENT_OFFSET, TIME_TO_LIVE, (byte) IPPROTO_ICMP,
+                (short) 0 /* checksum, calculate later */, srcIp, dstIp);
+        ipv4Header.writeToByteBuffer(packet);
+
+        // [3] ICMP header
+        final Icmpv4Header icmpv4Header = new Icmpv4Header((byte) type, ICMPECHO_CODE,
+                (short) 0 /* checksum, calculate later */);
+        icmpv4Header.writeToByteBuffer(packet);
+
+        // [4] Payload
+        packet.put(payload);
+        packet.flip();
+
+        // [5] Finalize packet
+        // Used for updating IP header fields. If there is Ehternet header, IPv4 header offset
+        // in buffer equals ethernet header length because IPv4 header is located next to ethernet
+        // header. Otherwise, IPv4 header offset is 0.
+        final int ipv4HeaderOffset = hasEther ? etherHeaderLen : 0;
+
+        // Populate the IPv4 totalLength field.
+        packet.putShort(ipv4HeaderOffset + IPV4_LENGTH_OFFSET,
+                (short) (ipv4HeaderLen + Icmpv4HeaderLen + payloadLen));
+
+        // Populate the IPv4 header checksum field.
+        packet.putShort(ipv4HeaderOffset + IPV4_CHECKSUM_OFFSET,
+                ipChecksum(packet, ipv4HeaderOffset /* headerOffset */));
+
+        // Populate the ICMP checksum field.
+        packet.putShort(ipv4HeaderOffset + IPV4_HEADER_MIN_LEN + ICMP_CHECKSUM_OFFSET,
+                icmpChecksum(packet, ipv4HeaderOffset + IPV4_HEADER_MIN_LEN,
+                        Icmpv4HeaderLen + payloadLen));
+        return packet;
+    }
+
+    @NonNull
+    private ByteBuffer buildIcmpEchoPacketV4(@NonNull final Inet4Address srcIp,
+            @NonNull final Inet4Address dstIp, int type, short id, short seq)
+            throws Exception {
+        return buildIcmpEchoPacketV4(null /* srcMac */, null /* dstMac */, srcIp, dstIp,
+                type, id, seq);
+    }
+
+    @Test
+    public void testIcmpv4Echo() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+        // TODO: remove the connectivity verification for upstream connected notification race.
+        // See the same reason in runUdp4Test().
+        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+        final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */,
+                tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */,
+                REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ);
+        tester.verifyUpload(request, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, false /* hasEth */, true /* isIpv4 */, ICMP_ECHO);
+        });
+
+        final ByteBuffer reply = buildIcmpEchoPacketV4(REMOTE_IP4_ADDR /* srcIp*/,
+                (Inet4Address) TEST_IP4_ADDR.getAddress() /* dstIp */, ICMP_ECHOREPLY, ICMPECHO_ID,
+                ICMPECHO_SEQ);
+        tester.verifyDownload(reply, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY);
+        });
+    }
+
+    // TODO: support R device. See b/234727688.
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testTetherClatIcmp() throws Exception {
+        // CLAT only starts on IPv6 only network.
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+
+        // Get CLAT IPv6 address.
+        final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered);
+
+        // Send an IPv4 ICMP packet in original direction.
+        // IPv4 packet -- CLAT translation --> IPv6 packet
+        final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */,
+                tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */,
+                (Inet4Address) REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ);
+        tester.verifyUpload(request, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */,
+                    ICMPV6_ECHO_REQUEST_TYPE);
+        });
+
+        // Send an IPv6 ICMP packet in reply direction.
+        // IPv6 packet -- CLAT translation --> IPv4 packet
+        final ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(
+                (Inet6Address) REMOTE_NAT64_ADDR /* srcIp */, clatIp6 /* dstIp */);
+        tester.verifyDownload(reply, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+
+            return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY);
+        });
+    }
+
+    @NonNull
+    private ByteBuffer buildDnsReplyMessageById(short id) {
+        byte[] replyMessage = Arrays.copyOf(DNS_REPLY, DNS_REPLY.length);
+        // Assign transaction id of reply message pattern with a given DNS transaction id.
+        replyMessage[0] = (byte) ((id >> 8) & 0xff);
+        replyMessage[1] = (byte) (id & 0xff);
+        Log.d(TAG, "Built DNS reply: " + dumpHexString(replyMessage));
+
+        return ByteBuffer.wrap(replyMessage);
+    }
+
+    @NonNull
+    private void sendDownloadPacketDnsV4(@NonNull final Inet4Address srcIp,
+            @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId,
+            @NonNull final TetheringTester tester) throws Exception {
+        // DNS response transaction id must be copied from DNS query. Used by the requester
+        // to match up replies to outstanding queries. See RFC 1035 section 4.1.1.
+        final ByteBuffer dnsReplyMessage = buildDnsReplyMessageById(dnsId);
+        final ByteBuffer testPacket = buildUdpPacket((InetAddress) srcIp,
+                (InetAddress) dstIp, srcPort, dstPort, dnsReplyMessage);
+
+        tester.verifyDownload(testPacket, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+            return isExpectedUdpDnsPacket(p, true /* hasEther */, true /* isIpv4 */,
+                    dnsReplyMessage);
+        });
+    }
+
+    // Send IPv4 UDP DNS packet and return the forwarded DNS packet on upstream.
+    @NonNull
+    private byte[] sendUploadPacketDnsV4(@NonNull final MacAddress srcMac,
+            @NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp,
+            @NonNull final Inet4Address dstIp, short srcPort, short dstPort,
+            @NonNull final TetheringTester tester) throws Exception {
+        final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
+                srcPort, dstPort, DNS_QUERY);
+
+        return tester.verifyUpload(testPacket, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+            return isExpectedUdpDnsPacket(p, false /* hasEther */, true /* isIpv4 */,
+                    DNS_QUERY);
+        });
+    }
+
+    @Test
+    public void testTetherUdpV4Dns() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+        // TODO: remove the connectivity verification for upstream connected notification race.
+        // See the same reason in runUdp4Test().
+        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+        // [1] Send DNS query.
+        // tethered device --> downstream --> dnsmasq forwarding --> upstream --> DNS server
+        //
+        // Need to extract DNS transaction id and source port from dnsmasq forwarded DNS query
+        // packet. dnsmasq forwarding creats new query which means UDP source port and DNS
+        // transaction id are changed from original sent DNS query. See forward_query() in
+        // external/dnsmasq/src/forward.c. Note that #TetheringTester.isExpectedUdpDnsPacket
+        // guarantees that |forwardedQueryPacket| is a valid DNS packet. So we can parse it as DNS
+        // packet.
+        final MacAddress srcMac = tethered.macAddr;
+        final MacAddress dstMac = tethered.routerMacAddr;
+        final Inet4Address clientIp = tethered.ipv4Addr;
+        final Inet4Address gatewayIp = tethered.ipv4Gatway;
+        final byte[] forwardedQueryPacket = sendUploadPacketDnsV4(srcMac, dstMac, clientIp,
+                gatewayIp, LOCAL_PORT, DNS_PORT, tester);
+        final ByteBuffer buf = ByteBuffer.wrap(forwardedQueryPacket);
+        Struct.parse(Ipv4Header.class, buf);
+        final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
+        final TestDnsPacket dnsQuery = TestDnsPacket.getTestDnsPacket(buf);
+        assertNotNull(dnsQuery);
+        Log.d(TAG, "Forwarded UDP source port: " + udpHeader.srcPort + ", DNS query id: "
+                + dnsQuery.getHeader().getId());
+
+        // [2] Send DNS reply.
+        // DNS server --> upstream --> dnsmasq forwarding --> downstream --> tethered device
+        //
+        // DNS reply transaction id must be copied from DNS query. Used by the requester to match
+        // up replies to outstanding queries. See RFC 1035 section 4.1.1.
+        final Inet4Address remoteIp = (Inet4Address) TEST_IP4_DNS;
+        final Inet4Address tetheringUpstreamIp = (Inet4Address) TEST_IP4_ADDR.getAddress();
+        sendDownloadPacketDnsV4(remoteIp, tetheringUpstreamIp, DNS_PORT,
+                (short) udpHeader.srcPort, (short) dnsQuery.getHeader().getId(), tester);
+    }
+
+    @Test
+    public void testTetherTcpV4() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+        // TODO: remove the connectivity verification for upstream connected notification race.
+        // See the same reason in runUdp4Test().
+        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
+
+        runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */,
+                tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */,
+                REMOTE_IP4_ADDR /* downloadSrcIp */, TEST_IP4_ADDR.getAddress() /* downloadDstIp */,
+                tester, false /* isClat */);
+    }
+
+    @Test
+    public void testTetherTcpV6() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+
+        runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */,
+                tethered.ipv6Addr /* uploadSrcIp */, REMOTE_IP6_ADDR /* uploadDstIp */,
+                REMOTE_IP6_ADDR /* downloadSrcIp */, tethered.ipv6Addr /* downloadDstIp */,
+                tester, false /* isClat */);
+    }
+
+    // TODO: support R device. See b/234727688.
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testTetherClatTcp() throws Exception {
+        // CLAT only starts on IPv6 only network.
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
+                toList(TEST_IP6_DNS));
+        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
+
+        // Get CLAT IPv6 address.
+        final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered);
+
+        runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */,
+                tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */,
+                REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */,
+                tester, true /* isClat */);
+    }
+}
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
deleted file mode 100644
index 880a285..0000000
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ /dev/null
@@ -1,1531 +0,0 @@
-/*
- * 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.
- */
-
-package android.net;
-
-import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.Manifest.permission.NETWORK_SETTINGS;
-import static android.Manifest.permission.TETHER_PRIVILEGED;
-import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
-import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
-import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringTester.TestDnsPacket;
-import static android.net.TetheringTester.isExpectedIcmpv6Packet;
-import static android.net.TetheringTester.isExpectedUdpDnsPacket;
-import static android.net.TetheringTester.isExpectedUdpPacket;
-import static android.system.OsConstants.IPPROTO_IP;
-import static android.system.OsConstants.IPPROTO_IPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
-import static com.android.net.module.util.HexDump.dumpHexString;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static com.android.testutils.DeviceInfoUtils.KVersion;
-import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.UiAutomation;
-import android.content.Context;
-import android.net.EthernetManager.TetheredInterfaceCallback;
-import android.net.EthernetManager.TetheredInterfaceRequest;
-import android.net.TetheringManager.StartTetheringCallback;
-import android.net.TetheringManager.TetheringEventCallback;
-import android.net.TetheringManager.TetheringRequest;
-import android.net.TetheringTester.TetheredDevice;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.VintfRuntimeInfo;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.net.module.util.BpfDump;
-import com.android.net.module.util.Ipv6Utils;
-import com.android.net.module.util.PacketBuilder;
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.bpf.Tether4Key;
-import com.android.net.module.util.bpf.Tether4Value;
-import com.android.net.module.util.bpf.TetherStatsKey;
-import com.android.net.module.util.bpf.TetherStatsValue;
-import com.android.net.module.util.structs.Ipv4Header;
-import com.android.net.module.util.structs.Ipv6Header;
-import com.android.net.module.util.structs.UdpHeader;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
-import com.android.testutils.DeviceInfoUtils;
-import com.android.testutils.DumpTestUtils;
-import com.android.testutils.HandlerUtils;
-import com.android.testutils.TapPacketReader;
-import com.android.testutils.TestNetworkTracker;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileDescriptor;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class EthernetTetheringTest {
-    @Rule
-    public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
-
-    private static final String TAG = EthernetTetheringTest.class.getSimpleName();
-    private static final int TIMEOUT_MS = 5000;
-    // Used to check if any tethering interface is available. Choose 200ms to be request timeout
-    // because the average interface requested time on cuttlefish@acloud is around 10ms.
-    // See TetheredInterfaceRequester.getInterface, isInterfaceForTetheringAvailable.
-    private static final int AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS = 200;
-    private static final int TETHER_REACHABILITY_ATTEMPTS = 20;
-    private static final int DUMP_POLLING_MAX_RETRY = 100;
-    private static final int DUMP_POLLING_INTERVAL_MS = 50;
-    // Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
-    // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
-    private static final int UDP_STREAM_TS_MS = 2000;
-    // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
-    private static final int RX_UDP_PACKET_SIZE = 30;
-    private static final int RX_UDP_PACKET_COUNT = 456;
-    // Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes.
-    private static final int TX_UDP_PACKET_SIZE = 44;
-    private static final int TX_UDP_PACKET_COUNT = 123;
-    private static final long WAIT_RA_TIMEOUT_MS = 2000;
-
-    private static final MacAddress TEST_MAC = MacAddress.fromString("1:2:3:4:5:6");
-    private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
-    private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
-    private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
-    private static final InetAddress TEST_IP6_DNS = parseNumericAddress("2001:db8:1::888");
-    private static final IpPrefix TEST_NAT64PREFIX = new IpPrefix("64:ff9b::/96");
-    private static final Inet6Address REMOTE_NAT64_ADDR =
-            (Inet6Address) parseNumericAddress("64:ff9b::808:808");
-    private static final Inet6Address REMOTE_IP6_ADDR =
-            (Inet6Address) parseNumericAddress("2002:db8:1::515:ca");
-    private static final ByteBuffer TEST_REACHABILITY_PAYLOAD =
-            ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
-
-    private static final short DNS_PORT = 53;
-
-    private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
-    private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
-    private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
-    private static final String LINE_DELIMITER = "\\n";
-
-    // version=6, traffic class=0x0, flowlabel=0x0;
-    private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
-    private static final short HOP_LIMIT = 0x40;
-
-    // TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports
-    // building packet for given arguments.
-    private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] {
-            // scapy.DNS(
-            //   id=0xbeef,
-            //   qr=0,
-            //   qd=scapy.DNSQR(qname="hello.example.com"))
-            //
-            /* Header */
-            (byte) 0xbe, (byte) 0xef, /* Transaction ID: 0xbeef */
-            (byte) 0x01, (byte) 0x00, /* Flags: rd */
-            (byte) 0x00, (byte) 0x01, /* Questions: 1 */
-            (byte) 0x00, (byte) 0x00, /* Answer RRs: 0 */
-            (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
-            (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
-            /* Queries */
-            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
-            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
-            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
-            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
-            (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */
-            (byte) 0x00, (byte) 0x01,              /* Type: A */
-            (byte) 0x00, (byte) 0x01               /* Class: IN */
-    });
-
-    private static final byte[] DNS_REPLY = new byte[] {
-            // scapy.DNS(
-            //   id=0,
-            //   qr=1,
-            //   qd=scapy.DNSQR(qname="hello.example.com"),
-            //   an=scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4'))
-            //
-            /* Header */
-            (byte) 0x00, (byte) 0x00, /* Transaction ID: 0x0, must be updated by dns query id */
-            (byte) 0x81, (byte) 0x00, /* Flags: qr rd */
-            (byte) 0x00, (byte) 0x01, /* Questions: 1 */
-            (byte) 0x00, (byte) 0x01, /* Answer RRs: 1 */
-            (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */
-            (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */
-            /* Queries */
-            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
-            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
-            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
-            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
-            (byte) 0x6f, (byte) 0x6d, (byte) 0x00,              /* Name: hello.example.com */
-            (byte) 0x00, (byte) 0x01,                           /* Type: A */
-            (byte) 0x00, (byte) 0x01,                           /* Class: IN */
-            /* Answers */
-            (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c,
-            (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65,
-            (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70,
-            (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63,
-            (byte) 0x6f, (byte) 0x6d, (byte) 0x00,              /* Name: hello.example.com */
-            (byte) 0x00, (byte) 0x01,                           /* Type: A */
-            (byte) 0x00, (byte) 0x01,                           /* Class: IN */
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* Time to live: 0 */
-            (byte) 0x00, (byte) 0x04,                           /* Data length: 4 */
-            (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04  /* Address: 1.2.3.4 */
-    };
-
-    private final Context mContext = InstrumentationRegistry.getContext();
-    private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
-    private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
-
-    private TestNetworkInterface mDownstreamIface;
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
-    private TapPacketReader mDownstreamReader;
-    private TapPacketReader mUpstreamReader;
-
-    private TetheredInterfaceRequester mTetheredInterfaceRequester;
-    private MyTetheringEventCallback mTetheringEventCallback;
-
-    private UiAutomation mUiAutomation =
-            InstrumentationRegistry.getInstrumentation().getUiAutomation();
-    private boolean mRunTests;
-
-    private TestNetworkTracker mUpstreamTracker;
-
-    @Before
-    public void setUp() throws Exception {
-        mHandlerThread = new HandlerThread(getClass().getSimpleName());
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-
-        mRunTests = runAsShell(NETWORK_SETTINGS, TETHER_PRIVILEGED, () ->
-                mTm.isTetheringSupported());
-        assumeTrue(mRunTests);
-
-        mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
-    }
-
-    private void cleanUp() throws Exception {
-        setPreferTestNetworks(false);
-
-        if (mUpstreamTracker != null) {
-            runAsShell(MANAGE_TEST_NETWORKS, () -> {
-                mUpstreamTracker.teardown();
-                mUpstreamTracker = null;
-            });
-        }
-        if (mUpstreamReader != null) {
-            TapPacketReader reader = mUpstreamReader;
-            mHandler.post(() -> reader.stop());
-            mUpstreamReader = null;
-        }
-
-        if (mDownstreamReader != null) {
-            TapPacketReader reader = mDownstreamReader;
-            mHandler.post(() -> reader.stop());
-            mDownstreamReader = null;
-        }
-
-        // To avoid flaky which caused by the next test started but the previous interface is not
-        // untracked from EthernetTracker yet. Just delete the test interface without explicitly
-        // calling TetheringManager#stopTethering could let EthernetTracker untrack the test
-        // interface from server mode before tethering stopped. Thus, awaitInterfaceUntethered
-        // could not only make sure tethering is stopped but also guarantee the test interface is
-        // untracked from EthernetTracker.
-        maybeDeleteTestInterface();
-        if (mTetheringEventCallback != null) {
-            mTetheringEventCallback.awaitInterfaceUntethered();
-            mTetheringEventCallback.unregister();
-            mTetheringEventCallback = null;
-        }
-        runAsShell(NETWORK_SETTINGS, () -> mTetheredInterfaceRequester.release());
-        setIncludeTestInterfaces(false);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        try {
-            if (mRunTests) cleanUp();
-        } finally {
-            mHandlerThread.quitSafely();
-            mUiAutomation.dropShellPermissionIdentity();
-        }
-    }
-
-    private boolean isInterfaceForTetheringAvailable() throws Exception {
-        // If previous test case doesn't release tethering interface successfully, the other tests
-        // after that test may be skipped as unexcepted.
-        // TODO: figure out a better way to check default tethering interface existenion.
-        final TetheredInterfaceRequester requester = new TetheredInterfaceRequester(mHandler, mEm);
-        try {
-            // Use short timeout (200ms) for requesting an existing interface, if any, because
-            // it should reurn faster than requesting a new tethering interface. Using default
-            // timeout (5000ms, TIMEOUT_MS) may make that total testing time is over 1 minute
-            // test module timeout on internal testing.
-            // TODO: if this becomes flaky, consider using default timeout (5000ms) and moving
-            // this check into #setUpOnce.
-            return requester.getInterface(AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS) != null;
-        } catch (TimeoutException e) {
-            return false;
-        } finally {
-            runAsShell(NETWORK_SETTINGS, () -> {
-                requester.release();
-            });
-        }
-    }
-
-    private void setIncludeTestInterfaces(boolean include) {
-        runAsShell(NETWORK_SETTINGS, () -> {
-            mEm.setIncludeTestInterfaces(include);
-        });
-    }
-
-    private void setPreferTestNetworks(boolean prefer) {
-        runAsShell(NETWORK_SETTINGS, () -> {
-            mTm.setPreferTestNetworks(prefer);
-        });
-    }
-
-    @Test
-    public void testVirtualEthernetAlreadyExists() throws Exception {
-        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
-        assumeFalse(isInterfaceForTetheringAvailable());
-
-        mDownstreamIface = createTestInterface();
-        // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
-        // interface will be placed in client mode, which will delete the link-local address.
-        // At that point NetworkInterface.getByName() will cease to work on the interface, because
-        // starting in R NetworkInterface can no longer see interfaces without IP addresses.
-        int mtu = getMTU(mDownstreamIface);
-
-        Log.d(TAG, "Including test interfaces");
-        setIncludeTestInterfaces(true);
-
-        final String iface = mTetheredInterfaceRequester.getInterface();
-        assertEquals("TetheredInterfaceCallback for unexpected interface",
-                mDownstreamIface.getInterfaceName(), iface);
-
-        checkVirtualEthernet(mDownstreamIface, mtu);
-    }
-
-    @Test
-    public void testVirtualEthernet() throws Exception {
-        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
-        assumeFalse(isInterfaceForTetheringAvailable());
-
-        CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
-
-        setIncludeTestInterfaces(true);
-
-        mDownstreamIface = createTestInterface();
-
-        final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertEquals("TetheredInterfaceCallback for unexpected interface",
-                mDownstreamIface.getInterfaceName(), iface);
-
-        checkVirtualEthernet(mDownstreamIface, getMTU(mDownstreamIface));
-    }
-
-    @Test
-    public void testStaticIpv4() throws Exception {
-        assumeFalse(isInterfaceForTetheringAvailable());
-
-        setIncludeTestInterfaces(true);
-
-        mDownstreamIface = createTestInterface();
-
-        final String iface = mTetheredInterfaceRequester.getInterface();
-        assertEquals("TetheredInterfaceCallback for unexpected interface",
-                mDownstreamIface.getInterfaceName(), iface);
-
-        assertInvalidStaticIpv4Request(iface, null, null);
-        assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
-        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
-        assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
-        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
-        assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
-        assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
-
-        final String localAddr = "192.0.2.3/28";
-        final String clientAddr = "192.0.2.2/28";
-        mTetheringEventCallback = enableEthernetTethering(iface,
-                requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */);
-
-        mTetheringEventCallback.awaitInterfaceTethered();
-        assertInterfaceHasIpAddress(iface, localAddr);
-
-        byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
-        byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
-
-        FileDescriptor fd = mDownstreamIface.getFileDescriptor().getFileDescriptor();
-        mDownstreamReader = makePacketReader(fd, getMTU(mDownstreamIface));
-        TetheringTester tester = new TetheringTester(mDownstreamReader);
-        DhcpResults dhcpResults = tester.runDhcp(client1);
-        assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
-
-        try {
-            tester.runDhcp(client2);
-            fail("Only one client should get an IP address");
-        } catch (TimeoutException expected) { }
-
-    }
-
-    private static void waitForRouterAdvertisement(TapPacketReader reader, String iface,
-            long timeoutMs) {
-        final long deadline = SystemClock.uptimeMillis() + timeoutMs;
-        do {
-            byte[] pkt = reader.popPacket(timeoutMs);
-            if (isExpectedIcmpv6Packet(pkt, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) return;
-
-            timeoutMs = deadline - SystemClock.uptimeMillis();
-        } while (timeoutMs > 0);
-        fail("Did not receive router advertisement on " + iface + " after "
-                +  timeoutMs + "ms idle");
-    }
-
-    private static void expectLocalOnlyAddresses(String iface) throws Exception {
-        final List<InterfaceAddress> interfaceAddresses =
-                NetworkInterface.getByName(iface).getInterfaceAddresses();
-
-        boolean foundIpv6Ula = false;
-        for (InterfaceAddress ia : interfaceAddresses) {
-            final InetAddress addr = ia.getAddress();
-            if (isIPv6ULA(addr)) {
-                foundIpv6Ula = true;
-            }
-            final int prefixlen = ia.getNetworkPrefixLength();
-            final LinkAddress la = new LinkAddress(addr, prefixlen);
-            if (la.isIpv6() && la.isGlobalPreferred()) {
-                fail("Found global IPv6 address on local-only interface: " + interfaceAddresses);
-            }
-        }
-
-        assertTrue("Did not find IPv6 ULA on local-only interface " + iface,
-                foundIpv6Ula);
-    }
-
-    @Test
-    public void testLocalOnlyTethering() throws Exception {
-        assumeFalse(isInterfaceForTetheringAvailable());
-
-        setIncludeTestInterfaces(true);
-
-        mDownstreamIface = createTestInterface();
-
-        final String iface = mTetheredInterfaceRequester.getInterface();
-        assertEquals("TetheredInterfaceCallback for unexpected interface",
-                mDownstreamIface.getInterfaceName(), iface);
-
-        final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET)
-                .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build();
-        mTetheringEventCallback = enableEthernetTethering(iface, request,
-                null /* any upstream */);
-        mTetheringEventCallback.awaitInterfaceLocalOnly();
-
-        // makePacketReader only works after tethering is started, because until then the interface
-        // does not have an IP address, and unprivileged apps cannot see interfaces without IP
-        // addresses. This shouldn't be flaky because the TAP interface will buffer all packets even
-        // before the reader is started.
-        mDownstreamReader = makePacketReader(mDownstreamIface);
-
-        waitForRouterAdvertisement(mDownstreamReader, iface, WAIT_RA_TIMEOUT_MS);
-        expectLocalOnlyAddresses(iface);
-    }
-
-    private boolean isAdbOverNetwork() {
-        // If adb TCP port opened, this test may running by adb over network.
-        return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
-                || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
-    }
-
-    @Test
-    public void testPhysicalEthernet() throws Exception {
-        assumeTrue(isInterfaceForTetheringAvailable());
-        // Do not run this test if adb is over network and ethernet is connected.
-        // It is likely the adb run over ethernet, the adb would break when ethernet is switching
-        // from client mode to server mode. See b/160389275.
-        assumeFalse(isAdbOverNetwork());
-
-        // Get an interface to use.
-        final String iface = mTetheredInterfaceRequester.getInterface();
-
-        // Enable Ethernet tethering and check that it starts.
-        mTetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */);
-
-        // There is nothing more we can do on a physical interface without connecting an actual
-        // client, which is not possible in this test.
-    }
-
-    private boolean isEthernetTetheringSupported() throws Exception {
-        final CompletableFuture<Boolean> future = new CompletableFuture<>();
-        final TetheringEventCallback callback = new TetheringEventCallback() {
-            @Override
-            public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
-                future.complete(supportedTypes.contains(TETHERING_ETHERNET));
-            }
-        };
-
-        try {
-            mTm.registerTetheringEventCallback(mHandler::post, callback);
-            return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        } finally {
-            mTm.unregisterTetheringEventCallback(callback);
-        }
-    }
-
-    private static final class MyTetheringEventCallback implements TetheringEventCallback {
-        private final TetheringManager mTm;
-        private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
-        private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1);
-        private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
-        private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
-        private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
-        private final CountDownLatch mUpstreamLatch = new CountDownLatch(1);
-        private final CountDownLatch mCallbackRegisteredLatch = new CountDownLatch(1);
-        private final TetheringInterface mIface;
-        private final Network mExpectedUpstream;
-
-        private boolean mAcceptAnyUpstream = false;
-
-        private volatile boolean mInterfaceWasTethered = false;
-        private volatile boolean mInterfaceWasLocalOnly = false;
-        private volatile boolean mUnregistered = false;
-        private volatile Collection<TetheredClient> mClients = null;
-        private volatile Network mUpstream = null;
-
-        MyTetheringEventCallback(TetheringManager tm, String iface) {
-            this(tm, iface, null);
-            mAcceptAnyUpstream = true;
-        }
-
-        MyTetheringEventCallback(TetheringManager tm, String iface, Network expectedUpstream) {
-            mTm = tm;
-            mIface = new TetheringInterface(TETHERING_ETHERNET, iface);
-            mExpectedUpstream = expectedUpstream;
-        }
-
-        public void unregister() {
-            mTm.unregisterTetheringEventCallback(this);
-            mUnregistered = true;
-        }
-        @Override
-        public void onTetheredInterfacesChanged(List<String> interfaces) {
-            fail("Should only call callback that takes a Set<TetheringInterface>");
-        }
-
-        @Override
-        public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
-            // Ignore stale callbacks registered by previous test cases.
-            if (mUnregistered) return;
-
-            if (!mInterfaceWasTethered && interfaces.contains(mIface)) {
-                // This interface is being tethered for the first time.
-                Log.d(TAG, "Tethering started: " + interfaces);
-                mInterfaceWasTethered = true;
-                mTetheringStartedLatch.countDown();
-            } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) {
-                Log.d(TAG, "Tethering stopped: " + interfaces);
-                mTetheringStoppedLatch.countDown();
-            }
-        }
-
-        @Override
-        public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
-            fail("Should only call callback that takes a Set<TetheringInterface>");
-        }
-
-        @Override
-        public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) {
-            // Ignore stale callbacks registered by previous test cases.
-            if (mUnregistered) return;
-
-            if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) {
-                // This interface is being put into local-only mode for the first time.
-                Log.d(TAG, "Local-only started: " + interfaces);
-                mInterfaceWasLocalOnly = true;
-                mLocalOnlyStartedLatch.countDown();
-            } else if (mInterfaceWasLocalOnly && !interfaces.contains(mIface)) {
-                Log.d(TAG, "Local-only stopped: " + interfaces);
-                mLocalOnlyStoppedLatch.countDown();
-            }
-        }
-
-        public void awaitInterfaceTethered() throws Exception {
-            assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms",
-                    mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        }
-
-        public void awaitInterfaceLocalOnly() throws Exception {
-            assertTrue("Ethernet not local-only after " + TIMEOUT_MS + "ms",
-                    mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        }
-
-        // Used to check if the callback has registered. When the callback is registered,
-        // onSupportedTetheringTypes is celled in onCallbackStarted(). After
-        // onSupportedTetheringTypes called, drop the permission for registering callback.
-        // See MyTetheringEventCallback#register, TetheringManager#onCallbackStarted.
-        @Override
-        public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
-            // Used to check callback registered.
-            mCallbackRegisteredLatch.countDown();
-        }
-
-        public void awaitCallbackRegistered() throws Exception {
-            if (!mCallbackRegisteredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                fail("Did not receive callback registered signal after " + TIMEOUT_MS + "ms");
-            }
-        }
-
-        public void awaitInterfaceUntethered() throws Exception {
-            // Don't block teardown if the interface was never tethered.
-            // This is racy because the interface might become tethered right after this check, but
-            // that can only happen in tearDown if startTethering timed out, which likely means
-            // the test has already failed.
-            if (!mInterfaceWasTethered && !mInterfaceWasLocalOnly) return;
-
-            if (mInterfaceWasTethered) {
-                assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
-                        mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            } else if (mInterfaceWasLocalOnly) {
-                assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
-                        mLocalOnlyStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            } else {
-                fail(mIface + " cannot be both tethered and local-only. Update this test class.");
-            }
-        }
-
-        @Override
-        public void onError(String ifName, int error) {
-            // Ignore stale callbacks registered by previous test cases.
-            if (mUnregistered) return;
-
-            fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
-        }
-
-        @Override
-        public void onClientsChanged(Collection<TetheredClient> clients) {
-            // Ignore stale callbacks registered by previous test cases.
-            if (mUnregistered) return;
-
-            Log.d(TAG, "Got clients changed: " + clients);
-            mClients = clients;
-            if (clients.size() > 0) {
-                mClientConnectedLatch.countDown();
-            }
-        }
-
-        public Collection<TetheredClient> awaitClientConnected() throws Exception {
-            assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms",
-                    mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            return mClients;
-        }
-
-        @Override
-        public void onUpstreamChanged(Network network) {
-            // Ignore stale callbacks registered by previous test cases.
-            if (mUnregistered) return;
-
-            Log.d(TAG, "Got upstream changed: " + network);
-            mUpstream = network;
-            if (mAcceptAnyUpstream || Objects.equals(mUpstream, mExpectedUpstream)) {
-                mUpstreamLatch.countDown();
-            }
-        }
-
-        public Network awaitUpstreamChanged() throws Exception {
-            if (!mUpstreamLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                fail("Did not receive upstream " + (mAcceptAnyUpstream ? "any" : mExpectedUpstream)
-                        + " callback after " + TIMEOUT_MS + "ms");
-            }
-            return mUpstream;
-        }
-    }
-
-    private MyTetheringEventCallback enableEthernetTethering(String iface,
-            TetheringRequest request, Network expectedUpstream) throws Exception {
-        // Enable ethernet tethering with null expectedUpstream means the test accept any upstream
-        // after etherent tethering started.
-        final MyTetheringEventCallback callback;
-        if (expectedUpstream != null) {
-            callback = new MyTetheringEventCallback(mTm, iface, expectedUpstream);
-        } else {
-            callback = new MyTetheringEventCallback(mTm, iface);
-        }
-        runAsShell(NETWORK_SETTINGS, () -> {
-            mTm.registerTetheringEventCallback(mHandler::post, callback);
-            // Need to hold the shell permission until callback is registered. This helps to avoid
-            // the test become flaky.
-            callback.awaitCallbackRegistered();
-        });
-        final CountDownLatch tetheringStartedLatch = new CountDownLatch(1);
-        StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
-            @Override
-            public void onTetheringStarted() {
-                Log.d(TAG, "Ethernet tethering started");
-                tetheringStartedLatch.countDown();
-            }
-
-            @Override
-            public void onTetheringFailed(int resultCode) {
-                fail("Unexpectedly got onTetheringFailed");
-            }
-        };
-        Log.d(TAG, "Starting Ethernet tethering");
-        runAsShell(TETHER_PRIVILEGED, () -> {
-            mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
-            // Binder call is an async call. Need to hold the shell permission until tethering
-            // started. This helps to avoid the test become flaky.
-            if (!tetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
-                fail("Did not receive tethering started callback after " + TIMEOUT_MS + "ms");
-            }
-        });
-
-        final int connectivityType = request.getConnectivityScope();
-        switch (connectivityType) {
-            case CONNECTIVITY_SCOPE_GLOBAL:
-                callback.awaitInterfaceTethered();
-                break;
-            case CONNECTIVITY_SCOPE_LOCAL:
-                callback.awaitInterfaceLocalOnly();
-                break;
-            default:
-                fail("Unexpected connectivity type requested: " + connectivityType);
-        }
-
-        return callback;
-    }
-
-    private MyTetheringEventCallback enableEthernetTethering(String iface, Network expectedUpstream)
-            throws Exception {
-        return enableEthernetTethering(iface,
-                new TetheringRequest.Builder(TETHERING_ETHERNET)
-                .setShouldShowEntitlementUi(false).build(), expectedUpstream);
-    }
-
-    private int getMTU(TestNetworkInterface iface) throws SocketException {
-        NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
-        assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
-        return nif.getMTU();
-    }
-
-    private TapPacketReader makePacketReader(final TestNetworkInterface iface) throws Exception {
-        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
-        return makePacketReader(fd, getMTU(iface));
-    }
-
-    private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
-        final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
-        mHandler.post(() -> reader.start());
-        HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
-        return reader;
-    }
-
-    private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
-        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
-        mDownstreamReader = makePacketReader(fd, mtu);
-        mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName(),
-                null /* any upstream */);
-        checkTetheredClientCallbacks(mDownstreamReader);
-    }
-
-    private void checkTetheredClientCallbacks(TapPacketReader packetReader) throws Exception {
-        // Create a fake client.
-        byte[] clientMacAddr = new byte[6];
-        new Random().nextBytes(clientMacAddr);
-
-        TetheringTester tester = new TetheringTester(packetReader);
-        DhcpResults dhcpResults = tester.runDhcp(clientMacAddr);
-
-        final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
-        assertEquals(1, clients.size());
-        final TetheredClient client = clients.iterator().next();
-
-        // Check the MAC address.
-        assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
-        assertEquals(TETHERING_ETHERNET, client.getTetheringType());
-
-        // Check the hostname.
-        assertEquals(1, client.getAddresses().size());
-        TetheredClient.AddressInfo info = client.getAddresses().get(0);
-        assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname());
-
-        // Check the address is the one that was handed out in the DHCP ACK.
-        assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
-
-        // Check that the lifetime is correct +/- 10s.
-        final long now = SystemClock.elapsedRealtime();
-        final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000;
-        final String msg = String.format("IP address should have lifetime of %d, got %d",
-                dhcpResults.leaseDuration, actualLeaseDuration);
-        assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
-    }
-
-    private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
-        private final Handler mHandler;
-        private final EthernetManager mEm;
-
-        private TetheredInterfaceRequest mRequest;
-        private final CompletableFuture<String> mFuture = new CompletableFuture<>();
-
-        TetheredInterfaceRequester(Handler handler, EthernetManager em) {
-            mHandler = handler;
-            mEm = em;
-        }
-
-        @Override
-        public void onAvailable(String iface) {
-            Log.d(TAG, "Ethernet interface available: " + iface);
-            mFuture.complete(iface);
-        }
-
-        @Override
-        public void onUnavailable() {
-            mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
-        }
-
-        public CompletableFuture<String> requestInterface() {
-            assertNull("BUG: more than one tethered interface request", mRequest);
-            Log.d(TAG, "Requesting tethered interface");
-            mRequest = runAsShell(NETWORK_SETTINGS, () ->
-                    mEm.requestTetheredInterface(mHandler::post, this));
-            return mFuture;
-        }
-
-        public String getInterface(int timeout) throws Exception {
-            return requestInterface().get(timeout, TimeUnit.MILLISECONDS);
-        }
-
-        public String getInterface() throws Exception {
-            return getInterface(TIMEOUT_MS);
-        }
-
-        public void release() {
-            if (mRequest != null) {
-                mFuture.obtrudeException(new IllegalStateException("Request already released"));
-                mRequest.release();
-                mRequest = null;
-            }
-        }
-    }
-
-    public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
-        // Check all fields except the deprecation and expiry times.
-        String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
-        assertTrue(msg, l1.isSameAddressAs(l2));
-        assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags());
-        assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
-    }
-
-    private TetheringRequest requestWithStaticIpv4(String local, String client) {
-        LinkAddress localAddr = local == null ? null : new LinkAddress(local);
-        LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
-        return new TetheringRequest.Builder(TETHERING_ETHERNET)
-                .setStaticIpv4Addresses(localAddr, clientAddr)
-                .setShouldShowEntitlementUi(false).build();
-    }
-
-    private void assertInvalidStaticIpv4Request(String iface, String local, String client)
-            throws Exception {
-        try {
-            enableEthernetTethering(iface, requestWithStaticIpv4(local, client),
-                    null /* any upstream */);
-            fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
-        } catch (IllegalArgumentException | NullPointerException expected) { }
-    }
-
-    private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
-        LinkAddress expectedAddr = new LinkAddress(expected);
-        NetworkInterface nif = NetworkInterface.getByName(iface);
-        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
-            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
-            if (expectedAddr.equals(addr)) {
-                return;
-            }
-        }
-        fail("Expected " + iface + " to have IP address " + expected + ", found "
-                + nif.getInterfaceAddresses());
-    }
-
-    private TestNetworkInterface createTestInterface() throws Exception {
-        TestNetworkManager tnm = runAsShell(MANAGE_TEST_NETWORKS, () ->
-                mContext.getSystemService(TestNetworkManager.class));
-        TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () ->
-                tnm.createTapInterface());
-        Log.d(TAG, "Created test interface " + iface.getInterfaceName());
-        return iface;
-    }
-
-    private void maybeDeleteTestInterface() throws Exception {
-        if (mDownstreamIface != null) {
-            mDownstreamIface.getFileDescriptor().close();
-            Log.d(TAG, "Deleted test interface " + mDownstreamIface.getInterfaceName());
-            mDownstreamIface = null;
-        }
-    }
-
-    private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses,
-            final List<InetAddress> dnses) throws Exception {
-        setPreferTestNetworks(true);
-
-        final LinkProperties lp = new LinkProperties();
-        lp.setLinkAddresses(addresses);
-        lp.setDnsServers(dnses);
-        lp.setNat64Prefix(TEST_NAT64PREFIX);
-
-        return runAsShell(MANAGE_TEST_NETWORKS, () -> initTestNetwork(mContext, lp, TIMEOUT_MS));
-    }
-
-    @Test
-    public void testIcmpv6Echo() throws Exception {
-        runPing6Test(initTetheringTester(toList(TEST_IP4_ADDR, TEST_IP6_ADDR),
-                toList(TEST_IP4_DNS, TEST_IP6_DNS)));
-    }
-
-    private void runPing6Test(TetheringTester tester) throws Exception {
-        TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
-        Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222");
-        ByteBuffer request = Ipv6Utils.buildEchoRequestPacket(tethered.macAddr,
-                tethered.routerMacAddr, tethered.ipv6Addr, remoteIp6Addr);
-        tester.verifyUpload(request, p -> {
-            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-
-            return isExpectedIcmpv6Packet(p, false /* hasEth */, ICMPV6_ECHO_REQUEST_TYPE);
-        });
-
-        ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(remoteIp6Addr, tethered.ipv6Addr);
-        tester.verifyDownload(reply, p -> {
-            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-
-            return isExpectedIcmpv6Packet(p, true /* hasEth */, ICMPV6_ECHO_REPLY_TYPE);
-        });
-    }
-
-    // Test network topology:
-    //
-    //         public network (rawip)                 private network
-    //                   |                 UE                |
-    // +------------+    V    +------------+------------+    V    +------------+
-    // |   Sever    +---------+  Upstream  | Downstream +---------+   Client   |
-    // +------------+         +------------+------------+         +------------+
-    // remote ip              public ip                           private ip
-    // 8.8.8.8:443            <Upstream ip>:9876                  <TetheredDevice ip>:9876
-    //
-    private static final Inet4Address REMOTE_IP4_ADDR =
-            (Inet4Address) parseNumericAddress("8.8.8.8");
-    // Used by public port and private port. Assume port 9876 has not been used yet before the
-    // testing that public port and private port are the same in the testing. Note that NAT port
-    // forwarding could be different between private port and public port.
-    // TODO: move to the start of test class.
-    private static final short LOCAL_PORT = 9876;
-    private static final short REMOTE_PORT = 433;
-    private static final byte TYPE_OF_SERVICE = 0;
-    private static final short ID = 27149;
-    private static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
-    private static final byte TIME_TO_LIVE = (byte) 0x40;
-    private static final ByteBuffer RX_PAYLOAD =
-            ByteBuffer.wrap(new byte[] { (byte) 0x12, (byte) 0x34 });
-    private static final ByteBuffer TX_PAYLOAD =
-            ByteBuffer.wrap(new byte[] { (byte) 0x56, (byte) 0x78 });
-
-    @NonNull
-    private ByteBuffer buildUdpPacket(
-            @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
-            @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
-            short srcPort, short dstPort, @Nullable final ByteBuffer payload)
-            throws Exception {
-        int ipProto;
-        short ethType;
-        if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) {
-            ipProto = IPPROTO_IP;
-            ethType = (short) ETHER_TYPE_IPV4;
-        } else if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) {
-            ipProto = IPPROTO_IPV6;
-            ethType = (short) ETHER_TYPE_IPV6;
-        } else {
-            fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
-            // Make compiler happy to the uninitialized ipProto and ethType.
-            return null;  // unreachable, the annotation @NonNull of function return value is true.
-        }
-
-        final boolean hasEther = (srcMac != null && dstMac != null);
-        final int payloadLen = (payload == null) ? 0 : payload.limit();
-        final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_UDP,
-                payloadLen);
-        final PacketBuilder packetBuilder = new PacketBuilder(buffer);
-
-        // [1] Ethernet header
-        if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ethType);
-
-        // [2] IP header
-        if (ipProto == IPPROTO_IP) {
-            packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
-                    TIME_TO_LIVE, (byte) IPPROTO_UDP, (Inet4Address) srcIp, (Inet4Address) dstIp);
-        } else {
-            packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_UDP,
-                    HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
-        }
-
-        // [3] UDP header
-        packetBuilder.writeUdpHeader(srcPort, dstPort);
-
-        // [4] Payload
-        if (payload != null) {
-            buffer.put(payload);
-            // in case data might be reused by caller, restore the position and
-            // limit of bytebuffer.
-            payload.clear();
-        }
-
-        return packetBuilder.finalizePacket();
-    }
-
-    @NonNull
-    private ByteBuffer buildUdpPacket(@NonNull final InetAddress srcIp,
-            @NonNull final InetAddress dstIp, short srcPort, short dstPort,
-            @Nullable final ByteBuffer payload) throws Exception {
-        return buildUdpPacket(null /* srcMac */, null /* dstMac */, srcIp, dstIp, srcPort,
-                dstPort, payload);
-    }
-
-    private boolean isAddressIpv4(@NonNull final  InetAddress srcIp,
-            @NonNull final InetAddress dstIp) {
-        if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
-        if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
-
-        fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
-        return false;  // unreachable
-    }
-
-    private void sendDownloadPacketUdp(@NonNull final InetAddress srcIp,
-            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
-            boolean is6To4) throws Exception {
-        if (is6To4) {
-            assertFalse("CLAT download test must sends IPv6 packet", isAddressIpv4(srcIp, dstIp));
-        }
-
-        // Expected received UDP packet IP protocol. While testing CLAT (is6To4 = true), the packet
-        // on downstream must be IPv4. Otherwise, the IP protocol of test packet is the same on
-        // both downstream and upstream.
-        final boolean isIpv4 = is6To4 ? true : isAddressIpv4(srcIp, dstIp);
-
-        final ByteBuffer testPacket = buildUdpPacket(srcIp, dstIp, REMOTE_PORT /* srcPort */,
-                LOCAL_PORT /* dstPort */, RX_PAYLOAD);
-        tester.verifyDownload(testPacket, p -> {
-            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, true /* hasEther */, isIpv4, RX_PAYLOAD);
-        });
-    }
-
-    private void sendUploadPacketUdp(@NonNull final MacAddress srcMac,
-            @NonNull final MacAddress dstMac, @NonNull final InetAddress srcIp,
-            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
-            boolean is4To6) throws Exception {
-        if (is4To6) {
-            assertTrue("CLAT upload test must sends IPv4 packet", isAddressIpv4(srcIp, dstIp));
-        }
-
-        // Expected received UDP packet IP protocol. While testing CLAT (is4To6 = true), the packet
-        // on upstream must be IPv6. Otherwise, the IP protocol of test packet is the same on
-        // both downstream and upstream.
-        final boolean isIpv4 = is4To6 ? false : isAddressIpv4(srcIp, dstIp);
-
-        final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
-                LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */, TX_PAYLOAD);
-        tester.verifyUpload(testPacket, p -> {
-            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-            return isExpectedUdpPacket(p, false /* hasEther */, isIpv4, TX_PAYLOAD);
-        });
-    }
-
-    @Test
-    public void testTetherUdpV6() throws Exception {
-        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
-                toList(TEST_IP6_DNS));
-        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
-        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr,
-                tethered.ipv6Addr, REMOTE_IP6_ADDR, tester, false /* is4To6 */);
-        sendDownloadPacketUdp(REMOTE_IP6_ADDR, tethered.ipv6Addr, tester, false /* is6To4 */);
-
-        // TODO: test BPF offload maps {rule, stats}.
-    }
-
-    // TODO: remove ipv4 verification (is4To6 = false) once upstream connected notification race is
-    // fixed. See #runUdp4Test.
-    //
-    // This function sends a probe packet to downstream interface and exam the result from upstream
-    // interface to make sure ipv4 tethering is ready. Return the entire packet which received from
-    // upstream interface.
-    @NonNull
-    private byte[] probeV4TetheringConnectivity(TetheringTester tester, TetheredDevice tethered,
-            boolean is4To6) throws Exception {
-        final ByteBuffer probePacket = buildUdpPacket(tethered.macAddr,
-                tethered.routerMacAddr, tethered.ipv4Addr /* srcIp */,
-                REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */,
-                TEST_REACHABILITY_PAYLOAD);
-
-        // Send a UDP packet from client and check the packet can be found on upstream interface.
-        for (int i = 0; i < TETHER_REACHABILITY_ATTEMPTS; i++) {
-            byte[] expectedPacket = tester.testUpload(probePacket, p -> {
-                Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-                // If is4To6 is true, the ipv4 probe packet would be translated to ipv6 by Clat and
-                // would see this translated ipv6 packet in upstream interface.
-                return isExpectedUdpPacket(p, false /* hasEther */, !is4To6 /* isIpv4 */,
-                        TEST_REACHABILITY_PAYLOAD);
-            });
-            if (expectedPacket != null) return expectedPacket;
-        }
-
-        fail("Can't verify " + (is4To6 ? "ipv4 to ipv6" : "ipv4") + " tethering connectivity after "
-                + TETHER_REACHABILITY_ATTEMPTS + " attempts");
-        return null;
-    }
-
-    private void runUdp4Test(boolean verifyBpf) throws Exception {
-        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
-                toList(TEST_IP4_DNS));
-        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
-
-        // TODO: remove the connectivity verification for upstream connected notification race.
-        // Because async upstream connected notification can't guarantee the tethering routing is
-        // ready to use. Need to test tethering connectivity before testing.
-        // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes
-        // from upstream. That can guarantee that the routing is ready. Long term plan is that
-        // refactors upstream connected notification from async to sync.
-        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
-
-        final MacAddress srcMac = tethered.macAddr;
-        final MacAddress dstMac = tethered.routerMacAddr;
-        final InetAddress remoteIp = REMOTE_IP4_ADDR;
-        final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress();
-        final InetAddress clientIp = tethered.ipv4Addr;
-        sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
-        sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
-
-        if (verifyBpf) {
-            // Send second UDP packet in original direction.
-            // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply"
-            // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make
-            // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay
-            // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds
-            // and apply ASSURED flag.
-            // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and
-            // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c
-            Thread.sleep(UDP_STREAM_TS_MS);
-            sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */);
-
-            // [1] Verify IPv4 upstream rule map.
-            final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
-                    Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
-            assertNotNull(upstreamMap);
-            assertEquals(1, upstreamMap.size());
-
-            final Map.Entry<Tether4Key, Tether4Value> rule =
-                    upstreamMap.entrySet().iterator().next();
-
-            final Tether4Key upstream4Key = rule.getKey();
-            assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
-            assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
-            assertEquals(LOCAL_PORT, upstream4Key.srcPort);
-            assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
-            assertEquals(REMOTE_PORT, upstream4Key.dstPort);
-
-            final Tether4Value upstream4Value = rule.getValue();
-            assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(),
-                    InetAddress.getByAddress(upstream4Value.src46).getAddress()));
-            assertEquals(LOCAL_PORT, upstream4Value.srcPort);
-            assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
-                    InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
-            assertEquals(REMOTE_PORT, upstream4Value.dstPort);
-
-            // [2] Verify stats map.
-            // Transmit packets on both direction for verifying stats. Because we only care the
-            // packet count in stats test, we just reuse the existing packets to increaes
-            // the packet count on both direction.
-
-            // Send packets on original direction.
-            for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
-                sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester,
-                        false /* is4To6 */);
-            }
-
-            // Send packets on reply direction.
-            for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
-                sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */);
-            }
-
-            // Dump stats map to verify.
-            final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
-                    TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
-            assertNotNull(statsMap);
-            assertEquals(1, statsMap.size());
-
-            final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
-                    statsMap.entrySet().iterator().next();
-
-            // TODO: verify the upstream index in TetherStatsKey.
-
-            final TetherStatsValue statsValue = stats.getValue();
-            assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
-            assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
-            assertEquals(0, statsValue.rxErrors);
-            assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
-            assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
-            assertEquals(0, statsValue.txErrors);
-        }
-    }
-
-    private TetheringTester initTetheringTester(List<LinkAddress> upstreamAddresses,
-            List<InetAddress> upstreamDnses) throws Exception {
-        assumeFalse(isInterfaceForTetheringAvailable());
-
-        // MyTetheringEventCallback currently only support await first available upstream. Tethering
-        // may select internet network as upstream if test network is not available and not be
-        // preferred yet. Create test upstream network before enable tethering.
-        mUpstreamTracker = createTestUpstream(upstreamAddresses, upstreamDnses);
-
-        mDownstreamIface = createTestInterface();
-        setIncludeTestInterfaces(true);
-
-        // Make sure EtherentTracker use "mDownstreamIface" as server mode interface.
-        assertEquals("TetheredInterfaceCallback for unexpected interface",
-                mDownstreamIface.getInterfaceName(), mTetheredInterfaceRequester.getInterface());
-
-        mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(),
-                mUpstreamTracker.getNetwork());
-        assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
-                mTetheringEventCallback.awaitUpstreamChanged());
-
-        mDownstreamReader = makePacketReader(mDownstreamIface);
-        mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());
-
-        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
-        // Currently tethering don't have API to tell when ipv6 tethering is available. Thus, make
-        // sure tethering already have ipv6 connectivity before testing.
-        if (cm.getLinkProperties(mUpstreamTracker.getNetwork()).hasGlobalIpv6Address()) {
-            waitForRouterAdvertisement(mDownstreamReader, mDownstreamIface.getInterfaceName(),
-                    WAIT_RA_TIMEOUT_MS);
-        }
-
-        return new TetheringTester(mDownstreamReader, mUpstreamReader);
-    }
-
-    private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
-        final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion);
-        return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0))
-                || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0))
-                || current.isAtLeast(new KVersion(5, 4, 98));
-    }
-
-    @Test
-    public void testIsUdpOffloadSupportedByKernel() throws Exception {
-        assertFalse(isUdpOffloadSupportedByKernel("4.14.221"));
-        assertTrue(isUdpOffloadSupportedByKernel("4.14.222"));
-        assertTrue(isUdpOffloadSupportedByKernel("4.16.0"));
-        assertTrue(isUdpOffloadSupportedByKernel("4.18.0"));
-        assertFalse(isUdpOffloadSupportedByKernel("4.19.0"));
-
-        assertFalse(isUdpOffloadSupportedByKernel("4.19.175"));
-        assertTrue(isUdpOffloadSupportedByKernel("4.19.176"));
-        assertTrue(isUdpOffloadSupportedByKernel("5.2.0"));
-        assertTrue(isUdpOffloadSupportedByKernel("5.3.0"));
-        assertFalse(isUdpOffloadSupportedByKernel("5.4.0"));
-
-        assertFalse(isUdpOffloadSupportedByKernel("5.4.97"));
-        assertTrue(isUdpOffloadSupportedByKernel("5.4.98"));
-        assertTrue(isUdpOffloadSupportedByKernel("5.10.0"));
-    }
-
-    private static void assumeKernelSupportBpfOffloadUdpV4() {
-        final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
-        assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload",
-                isUdpOffloadSupportedByKernel(kernelVersion));
-    }
-
-    @Test
-    public void testKernelSupportBpfOffloadUdpV4() throws Exception {
-        assumeKernelSupportBpfOffloadUdpV4();
-    }
-
-    @Test
-    public void testTetherConfigBpfOffloadEnabled() throws Exception {
-        assumeTrue(isTetherConfigBpfOffloadEnabled());
-    }
-
-    /**
-     * Basic IPv4 UDP tethering test. Verify that UDP tethered packets are transferred no matter
-     * using which data path.
-     */
-    @Test
-    public void testTetherUdpV4() throws Exception {
-        runUdp4Test(false /* verifyBpf */);
-    }
-
-    /**
-     * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF.
-     * Minimum test requirement:
-     * 1. S+ device.
-     * 2. Tethering config enables tethering BPF offload.
-     * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel.
-     *
-     * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883
-     */
-    @Test
-    @IgnoreUpTo(Build.VERSION_CODES.R)
-    public void testTetherUdpV4_VerifyBpf() throws Exception {
-        assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled());
-        assumeKernelSupportBpfOffloadUdpV4();
-
-        runUdp4Test(true /* verifyBpf */);
-    }
-
-    @NonNull
-    private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
-            Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
-            throws Exception {
-        final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
-        final String rawMapStr = runAsShell(DUMP, () ->
-                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
-        final HashMap<K, V> map = new HashMap<>();
-
-        for (final String line : rawMapStr.split(LINE_DELIMITER)) {
-            final Pair<K, V> rule =
-                    BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim());
-            map.put(rule.first, rule.second);
-        }
-        return map;
-    }
-
-    @Nullable
-    private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
-            Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
-            throws Exception {
-        for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
-            final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
-            if (!map.isEmpty()) return map;
-
-            Thread.sleep(DUMP_POLLING_INTERVAL_MS);
-        }
-
-        fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms");
-        return null;
-    }
-
-    private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
-        final String dumpStr = runAsShell(DUMP, () ->
-                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
-
-        // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
-        // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
-        // RRO to override the enabled default value. Get the tethering config via dumpsys.
-        // $ dumpsys tethering
-        //   mIsBpfEnabled: true
-        boolean enabled = dumpStr.contains("mIsBpfEnabled: true");
-        if (!enabled) {
-            Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr);
-        }
-        return enabled;
-    }
-
-    @NonNull
-    private Inet6Address getClatIpv6Address(TetheringTester tester, TetheredDevice tethered)
-            throws Exception {
-        // Send an IPv4 UDP packet from client and check that a CLAT translated IPv6 UDP packet can
-        // be found on upstream interface. Get CLAT IPv6 address from the CLAT translated IPv6 UDP
-        // packet.
-        byte[] expectedPacket = probeV4TetheringConnectivity(tester, tethered, true /* is4To6 */);
-
-        // Above has guaranteed that the found packet is an IPv6 packet without ether header.
-        return Struct.parse(Ipv6Header.class, ByteBuffer.wrap(expectedPacket)).srcIp;
-    }
-
-    // Test network topology:
-    //
-    //            public network (rawip)                 private network
-    //                      |         UE (CLAT support)         |
-    // +---------------+    V    +------------+------------+    V    +------------+
-    // | NAT64 Gateway +---------+  Upstream  | Downstream +---------+   Client   |
-    // +---------------+         +------------+------------+         +------------+
-    // remote ip                 public ip                           private ip
-    // [64:ff9b::808:808]:443    [clat ipv6]:9876                    [TetheredDevice ipv4]:9876
-    //
-    // Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by
-    // sending out an IPv4 packet and extracting the source address from CLAT translated IPv6
-    // packet.
-    //
-    private void runClatUdpTest() throws Exception {
-        // CLAT only starts on IPv6 only network.
-        final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR),
-                toList(TEST_IP6_DNS));
-        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */);
-
-        // Get CLAT IPv6 address.
-        final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered);
-
-        // Send an IPv4 UDP packet in original direction.
-        // IPv4 packet -- CLAT translation --> IPv6 packet
-        sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr,
-                REMOTE_IP4_ADDR, tester, true /* is4To6 */);
-
-        // Send an IPv6 UDP packet in reply direction.
-        // IPv6 packet -- CLAT translation --> IPv4 packet
-        sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */);
-
-        // TODO: test CLAT bpf maps.
-    }
-
-    @Test
-    @IgnoreUpTo(Build.VERSION_CODES.R)
-    public void testTetherClatUdp() throws Exception {
-        runClatUdpTest();
-    }
-
-    @NonNull
-    private ByteBuffer buildDnsReplyMessageById(short id) {
-        byte[] replyMessage = Arrays.copyOf(DNS_REPLY, DNS_REPLY.length);
-        // Assign transaction id of reply message pattern with a given DNS transaction id.
-        replyMessage[0] = (byte) ((id >> 8) & 0xff);
-        replyMessage[1] = (byte) (id & 0xff);
-        Log.d(TAG, "Built DNS reply: " + dumpHexString(replyMessage));
-
-        return ByteBuffer.wrap(replyMessage);
-    }
-
-    @NonNull
-    private void sendDownloadPacketDnsV4(@NonNull final Inet4Address srcIp,
-            @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId,
-            @NonNull final TetheringTester tester) throws Exception {
-        // DNS response transaction id must be copied from DNS query. Used by the requester
-        // to match up replies to outstanding queries. See RFC 1035 section 4.1.1.
-        final ByteBuffer dnsReplyMessage = buildDnsReplyMessageById(dnsId);
-        final ByteBuffer testPacket = buildUdpPacket((InetAddress) srcIp,
-                (InetAddress) dstIp, srcPort, dstPort, dnsReplyMessage);
-
-        tester.verifyDownload(testPacket, p -> {
-            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
-            return isExpectedUdpDnsPacket(p, true /* hasEther */, true /* isIpv4 */,
-                    dnsReplyMessage);
-        });
-    }
-
-    // Send IPv4 UDP DNS packet and return the forwarded DNS packet on upstream.
-    @NonNull
-    private byte[] sendUploadPacketDnsV4(@NonNull final MacAddress srcMac,
-            @NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp,
-            @NonNull final Inet4Address dstIp, short srcPort, short dstPort,
-            @NonNull final TetheringTester tester) throws Exception {
-        final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
-                srcPort, dstPort, DNS_QUERY);
-
-        return tester.verifyUpload(testPacket, p -> {
-            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
-            return isExpectedUdpDnsPacket(p, false /* hasEther */, true /* isIpv4 */,
-                    DNS_QUERY);
-        });
-    }
-
-    @Test
-    public void testTetherUdpV4Dns() throws Exception {
-        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
-                toList(TEST_IP4_DNS));
-        final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
-
-        // TODO: remove the connectivity verification for upstream connected notification race.
-        // See the same reason in runUdp4Test().
-        probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */);
-
-        // [1] Send DNS query.
-        // tethered device --> downstream --> dnsmasq forwarding --> upstream --> DNS server
-        //
-        // Need to extract DNS transaction id and source port from dnsmasq forwarded DNS query
-        // packet. dnsmasq forwarding creats new query which means UDP source port and DNS
-        // transaction id are changed from original sent DNS query. See forward_query() in
-        // external/dnsmasq/src/forward.c. Note that #TetheringTester.isExpectedUdpDnsPacket
-        // guarantees that |forwardedQueryPacket| is a valid DNS packet. So we can parse it as DNS
-        // packet.
-        final MacAddress srcMac = tethered.macAddr;
-        final MacAddress dstMac = tethered.routerMacAddr;
-        final Inet4Address clientIp = tethered.ipv4Addr;
-        final Inet4Address gatewayIp = tethered.ipv4Gatway;
-        final byte[] forwardedQueryPacket = sendUploadPacketDnsV4(srcMac, dstMac, clientIp,
-                gatewayIp, LOCAL_PORT, DNS_PORT, tester);
-        final ByteBuffer buf = ByteBuffer.wrap(forwardedQueryPacket);
-        Struct.parse(Ipv4Header.class, buf);
-        final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
-        final TestDnsPacket dnsQuery = TestDnsPacket.getTestDnsPacket(buf);
-        assertNotNull(dnsQuery);
-        Log.d(TAG, "Forwarded UDP source port: " + udpHeader.srcPort + ", DNS query id: "
-                + dnsQuery.getHeader().id);
-
-        // [2] Send DNS reply.
-        // DNS server --> upstream --> dnsmasq forwarding --> downstream --> tethered device
-        //
-        // DNS reply transaction id must be copied from DNS query. Used by the requester to match
-        // up replies to outstanding queries. See RFC 1035 section 4.1.1.
-        final Inet4Address remoteIp = (Inet4Address) TEST_IP4_DNS;
-        final Inet4Address tetheringUpstreamIp = (Inet4Address) TEST_IP4_ADDR.getAddress();
-        sendDownloadPacketDnsV4(remoteIp, tetheringUpstreamIp, DNS_PORT,
-                (short) udpHeader.srcPort, (short) dnsQuery.getHeader().id, tester);
-    }
-
-    private <T> List<T> toList(T... array) {
-        return Arrays.asList(array);
-    }
-}
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTestBase.java
new file mode 100644
index 0000000..7685981
--- /dev/null
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTestBase.java
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (C) 2022 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 android.net;
+
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringTester.isExpectedIcmpPacket;
+import static android.net.TetheringTester.isExpectedTcpPacket;
+import static android.net.TetheringTester.isExpectedUdpPacket;
+import static android.system.OsConstants.IPPROTO_IP;
+import static android.system.OsConstants.IPPROTO_IPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static com.android.net.module.util.HexDump.dumpHexString;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.TCPHDR_ACK;
+import static com.android.net.module.util.NetworkStackConstants.TCPHDR_SYN;
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.EthernetManager.TetheredInterfaceCallback;
+import android.net.EthernetManager.TetheredInterfaceRequest;
+import android.net.TetheringManager.StartTetheringCallback;
+import android.net.TetheringManager.TetheringEventCallback;
+import android.net.TetheringManager.TetheringRequest;
+import android.net.TetheringTester.TetheredDevice;
+import android.net.cts.util.CtsNetUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.PacketBuilder;
+import com.android.testutils.HandlerUtils;
+import com.android.testutils.TapPacketReader;
+import com.android.testutils.TestNetworkTracker;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * TODO: Common variables or methods shared between CtsEthernetTetheringTest and
+ * MtsEthernetTetheringTest.
+ */
+public abstract class EthernetTetheringTestBase {
+    private static final String TAG = EthernetTetheringTestBase.class.getSimpleName();
+
+    protected static final int TIMEOUT_MS = 5000;
+    // Used to check if any tethering interface is available. Choose 200ms to be request timeout
+    // because the average interface requested time on cuttlefish@acloud is around 10ms.
+    // See TetheredInterfaceRequester.getInterface, isInterfaceForTetheringAvailable.
+    private static final int AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS = 200;
+    private static final int TETHER_REACHABILITY_ATTEMPTS = 20;
+    protected static final long WAIT_RA_TIMEOUT_MS = 2000;
+
+    // Address and NAT prefix definition.
+    protected static final MacAddress TEST_MAC = MacAddress.fromString("1:2:3:4:5:6");
+    protected static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/24");
+    protected static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
+    protected static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
+    protected static final InetAddress TEST_IP6_DNS = parseNumericAddress("2001:db8:1::888");
+
+    protected static final Inet4Address REMOTE_IP4_ADDR =
+            (Inet4Address) parseNumericAddress("8.8.8.8");
+    protected static final Inet6Address REMOTE_IP6_ADDR =
+            (Inet6Address) parseNumericAddress("2002:db8:1::515:ca");
+    protected static final Inet6Address REMOTE_NAT64_ADDR =
+            (Inet6Address) parseNumericAddress("64:ff9b::808:808");
+    protected static final IpPrefix TEST_NAT64PREFIX = new IpPrefix("64:ff9b::/96");
+
+    // IPv4 header definition.
+    protected static final short ID = 27149;
+    protected static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
+    protected static final byte TIME_TO_LIVE = (byte) 0x40;
+    protected static final byte TYPE_OF_SERVICE = 0;
+
+    // IPv6 header definition.
+    private static final short HOP_LIMIT = 0x40;
+    // version=6, traffic class=0x0, flowlabel=0x0;
+    private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
+
+    // UDP and TCP header definition.
+    // LOCAL_PORT is used by public port and private port. Assume port 9876 has not been used yet
+    // before the testing that public port and private port are the same in the testing. Note that
+    // NAT port forwarding could be different between private port and public port.
+    protected static final short LOCAL_PORT = 9876;
+    protected static final short REMOTE_PORT = 433;
+    private static final short WINDOW = (short) 0x2000;
+    private static final short URGENT_POINTER = 0;
+
+    // Payload definition.
+    protected static final ByteBuffer EMPTY_PAYLOAD = ByteBuffer.wrap(new byte[0]);
+    private static final ByteBuffer TEST_REACHABILITY_PAYLOAD =
+            ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
+    protected static final ByteBuffer RX_PAYLOAD =
+            ByteBuffer.wrap(new byte[] { (byte) 0x12, (byte) 0x34 });
+    protected static final ByteBuffer TX_PAYLOAD =
+            ByteBuffer.wrap(new byte[] { (byte) 0x56, (byte) 0x78 });
+
+    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
+    private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
+    private final PackageManager mPackageManager = mContext.getPackageManager();
+    private final CtsNetUtils mCtsNetUtils = new CtsNetUtils(mContext);
+    private final UiAutomation mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    // Late initialization in setUp()
+    private boolean mRunTests;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private TetheredInterfaceRequester mTetheredInterfaceRequester;
+
+    // Late initialization in initTetheringTester().
+    private TapPacketReader mUpstreamReader;
+    private TestNetworkTracker mUpstreamTracker;
+    private TestNetworkInterface mDownstreamIface;
+    private TapPacketReader mDownstreamReader;
+    private MyTetheringEventCallback mTetheringEventCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mHandlerThread = new HandlerThread(getClass().getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+
+        mRunTests = runAsShell(NETWORK_SETTINGS, TETHER_PRIVILEGED, () ->
+                mTm.isTetheringSupported());
+        assumeTrue(mRunTests);
+
+        mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
+    }
+
+    protected void maybeStopTapPacketReader(final TapPacketReader tapPacketReader)
+            throws Exception {
+        if (tapPacketReader != null) {
+            TapPacketReader reader = tapPacketReader;
+            mHandler.post(() -> reader.stop());
+        }
+    }
+
+    protected void maybeCloseTestInterface(final TestNetworkInterface testInterface)
+            throws Exception {
+        if (testInterface != null) {
+            testInterface.getFileDescriptor().close();
+            Log.d(TAG, "Deleted test interface " + testInterface.getInterfaceName());
+        }
+    }
+
+    protected void maybeUnregisterTetheringEventCallback(final MyTetheringEventCallback callback)
+            throws Exception {
+        if (callback != null) {
+            callback.awaitInterfaceUntethered();
+            callback.unregister();
+        }
+    }
+
+    protected void cleanUp() throws Exception {
+        setPreferTestNetworks(false);
+
+        if (mUpstreamTracker != null) {
+            runAsShell(MANAGE_TEST_NETWORKS, () -> {
+                mUpstreamTracker.teardown();
+                mUpstreamTracker = null;
+            });
+        }
+        if (mUpstreamReader != null) {
+            TapPacketReader reader = mUpstreamReader;
+            mHandler.post(() -> reader.stop());
+            mUpstreamReader = null;
+        }
+
+        maybeStopTapPacketReader(mDownstreamReader);
+        mDownstreamReader = null;
+        // To avoid flaky which caused by the next test started but the previous interface is not
+        // untracked from EthernetTracker yet. Just delete the test interface without explicitly
+        // calling TetheringManager#stopTethering could let EthernetTracker untrack the test
+        // interface from server mode before tethering stopped. Thus, awaitInterfaceUntethered
+        // could not only make sure tethering is stopped but also guarantee the test interface is
+        // untracked from EthernetTracker.
+        maybeCloseTestInterface(mDownstreamIface);
+        mDownstreamIface = null;
+        maybeUnregisterTetheringEventCallback(mTetheringEventCallback);
+        mTetheringEventCallback = null;
+
+        runAsShell(NETWORK_SETTINGS, () -> mTetheredInterfaceRequester.release());
+        setIncludeTestInterfaces(false);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        try {
+            if (mRunTests) cleanUp();
+        } finally {
+            mHandlerThread.quitSafely();
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    protected boolean isInterfaceForTetheringAvailable() throws Exception {
+        // Before T, all ethernet interfaces could be used for server mode. Instead of
+        // waiting timeout, just checking whether the system currently has any
+        // ethernet interface is more reliable.
+        if (!SdkLevel.isAtLeastT()) {
+            return runAsShell(CONNECTIVITY_USE_RESTRICTED_NETWORKS, () -> mEm.isAvailable());
+        }
+
+        // If previous test case doesn't release tethering interface successfully, the other tests
+        // after that test may be skipped as unexcepted.
+        // TODO: figure out a better way to check default tethering interface existenion.
+        final TetheredInterfaceRequester requester = new TetheredInterfaceRequester(mHandler, mEm);
+        try {
+            // Use short timeout (200ms) for requesting an existing interface, if any, because
+            // it should reurn faster than requesting a new tethering interface. Using default
+            // timeout (5000ms, TIMEOUT_MS) may make that total testing time is over 1 minute
+            // test module timeout on internal testing.
+            // TODO: if this becomes flaky, consider using default timeout (5000ms) and moving
+            // this check into #setUpOnce.
+            return requester.getInterface(AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS) != null;
+        } catch (TimeoutException e) {
+            return false;
+        } finally {
+            runAsShell(NETWORK_SETTINGS, () -> {
+                requester.release();
+            });
+        }
+    }
+
+    protected void setIncludeTestInterfaces(boolean include) {
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mEm.setIncludeTestInterfaces(include);
+        });
+    }
+
+    protected void setPreferTestNetworks(boolean prefer) {
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mTm.setPreferTestNetworks(prefer);
+        });
+    }
+
+    protected String getTetheredInterface() throws Exception {
+        return mTetheredInterfaceRequester.getInterface();
+    }
+
+    protected CompletableFuture<String> requestTetheredInterface() throws Exception {
+        return mTetheredInterfaceRequester.requestInterface();
+    }
+
+    protected static void waitForRouterAdvertisement(TapPacketReader reader, String iface,
+            long timeoutMs) {
+        final long deadline = SystemClock.uptimeMillis() + timeoutMs;
+        do {
+            byte[] pkt = reader.popPacket(timeoutMs);
+            if (isExpectedIcmpPacket(pkt, true /* hasEth */, false /* isIpv4 */,
+                    ICMPV6_ROUTER_ADVERTISEMENT)) {
+                return;
+            }
+
+            timeoutMs = deadline - SystemClock.uptimeMillis();
+        } while (timeoutMs > 0);
+        fail("Did not receive router advertisement on " + iface + " after "
+                +  timeoutMs + "ms idle");
+    }
+
+
+    protected static final class MyTetheringEventCallback implements TetheringEventCallback {
+        private final TetheringManager mTm;
+        private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
+        private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1);
+        private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
+        private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
+        private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
+        private final CountDownLatch mUpstreamLatch = new CountDownLatch(1);
+        private final CountDownLatch mCallbackRegisteredLatch = new CountDownLatch(1);
+        private final TetheringInterface mIface;
+        private final Network mExpectedUpstream;
+
+        private boolean mAcceptAnyUpstream = false;
+
+        private volatile boolean mInterfaceWasTethered = false;
+        private volatile boolean mInterfaceWasLocalOnly = false;
+        private volatile boolean mUnregistered = false;
+        private volatile Collection<TetheredClient> mClients = null;
+        private volatile Network mUpstream = null;
+
+        MyTetheringEventCallback(TetheringManager tm, String iface) {
+            this(tm, iface, null);
+            mAcceptAnyUpstream = true;
+        }
+
+        MyTetheringEventCallback(TetheringManager tm, String iface, Network expectedUpstream) {
+            mTm = tm;
+            mIface = new TetheringInterface(TETHERING_ETHERNET, iface);
+            mExpectedUpstream = expectedUpstream;
+        }
+
+        public void unregister() {
+            mTm.unregisterTetheringEventCallback(this);
+            mUnregistered = true;
+        }
+        @Override
+        public void onTetheredInterfacesChanged(List<String> interfaces) {
+            fail("Should only call callback that takes a Set<TetheringInterface>");
+        }
+
+        @Override
+        public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            if (!mInterfaceWasTethered && interfaces.contains(mIface)) {
+                // This interface is being tethered for the first time.
+                Log.d(TAG, "Tethering started: " + interfaces);
+                mInterfaceWasTethered = true;
+                mTetheringStartedLatch.countDown();
+            } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) {
+                Log.d(TAG, "Tethering stopped: " + interfaces);
+                mTetheringStoppedLatch.countDown();
+            }
+        }
+
+        @Override
+        public void onLocalOnlyInterfacesChanged(List<String> interfaces) {
+            fail("Should only call callback that takes a Set<TetheringInterface>");
+        }
+
+        @Override
+        public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) {
+                // This interface is being put into local-only mode for the first time.
+                Log.d(TAG, "Local-only started: " + interfaces);
+                mInterfaceWasLocalOnly = true;
+                mLocalOnlyStartedLatch.countDown();
+            } else if (mInterfaceWasLocalOnly && !interfaces.contains(mIface)) {
+                Log.d(TAG, "Local-only stopped: " + interfaces);
+                mLocalOnlyStoppedLatch.countDown();
+            }
+        }
+
+        public void awaitInterfaceTethered() throws Exception {
+            assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms",
+                    mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+
+        public void awaitInterfaceLocalOnly() throws Exception {
+            assertTrue("Ethernet not local-only after " + TIMEOUT_MS + "ms",
+                    mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+
+        // Used to check if the callback has registered. When the callback is registered,
+        // onSupportedTetheringTypes is celled in onCallbackStarted(). After
+        // onSupportedTetheringTypes called, drop the permission for registering callback.
+        // See MyTetheringEventCallback#register, TetheringManager#onCallbackStarted.
+        @Override
+        public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
+            // Used to check callback registered.
+            mCallbackRegisteredLatch.countDown();
+        }
+
+        public void awaitCallbackRegistered() throws Exception {
+            if (!mCallbackRegisteredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                fail("Did not receive callback registered signal after " + TIMEOUT_MS + "ms");
+            }
+        }
+
+        public void awaitInterfaceUntethered() throws Exception {
+            // Don't block teardown if the interface was never tethered.
+            // This is racy because the interface might become tethered right after this check, but
+            // that can only happen in tearDown if startTethering timed out, which likely means
+            // the test has already failed.
+            if (!mInterfaceWasTethered && !mInterfaceWasLocalOnly) return;
+
+            if (mInterfaceWasTethered) {
+                assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
+                        mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            } else if (mInterfaceWasLocalOnly) {
+                assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
+                        mLocalOnlyStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            } else {
+                fail(mIface + " cannot be both tethered and local-only. Update this test class.");
+            }
+        }
+
+        @Override
+        public void onError(String ifName, int error) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
+        }
+
+        @Override
+        public void onClientsChanged(Collection<TetheredClient> clients) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            Log.d(TAG, "Got clients changed: " + clients);
+            mClients = clients;
+            if (clients.size() > 0) {
+                mClientConnectedLatch.countDown();
+            }
+        }
+
+        public Collection<TetheredClient> awaitClientConnected() throws Exception {
+            assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms",
+                    mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            return mClients;
+        }
+
+        @Override
+        public void onUpstreamChanged(Network network) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            Log.d(TAG, "Got upstream changed: " + network);
+            mUpstream = network;
+            if (mAcceptAnyUpstream || Objects.equals(mUpstream, mExpectedUpstream)) {
+                mUpstreamLatch.countDown();
+            }
+        }
+
+        public Network awaitUpstreamChanged(boolean throwTimeoutException) throws Exception {
+            if (!mUpstreamLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                final String errorMessage = "Did not receive upstream "
+                            + (mAcceptAnyUpstream ? "any" : mExpectedUpstream)
+                            + " callback after " + TIMEOUT_MS + "ms";
+
+                if (throwTimeoutException) {
+                    throw new TimeoutException(errorMessage);
+                } else {
+                    fail(errorMessage);
+                }
+            }
+            return mUpstream;
+        }
+    }
+
+    protected MyTetheringEventCallback enableEthernetTethering(String iface,
+            TetheringRequest request, Network expectedUpstream) throws Exception {
+        // Enable ethernet tethering with null expectedUpstream means the test accept any upstream
+        // after etherent tethering started.
+        final MyTetheringEventCallback callback;
+        if (expectedUpstream != null) {
+            callback = new MyTetheringEventCallback(mTm, iface, expectedUpstream);
+        } else {
+            callback = new MyTetheringEventCallback(mTm, iface);
+        }
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mTm.registerTetheringEventCallback(mHandler::post, callback);
+            // Need to hold the shell permission until callback is registered. This helps to avoid
+            // the test become flaky.
+            callback.awaitCallbackRegistered();
+        });
+        final CountDownLatch tetheringStartedLatch = new CountDownLatch(1);
+        StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
+            @Override
+            public void onTetheringStarted() {
+                Log.d(TAG, "Ethernet tethering started");
+                tetheringStartedLatch.countDown();
+            }
+
+            @Override
+            public void onTetheringFailed(int resultCode) {
+                fail("Unexpectedly got onTetheringFailed");
+            }
+        };
+        Log.d(TAG, "Starting Ethernet tethering");
+        runAsShell(TETHER_PRIVILEGED, () -> {
+            mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
+            // Binder call is an async call. Need to hold the shell permission until tethering
+            // started. This helps to avoid the test become flaky.
+            if (!tetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                fail("Did not receive tethering started callback after " + TIMEOUT_MS + "ms");
+            }
+        });
+
+        final int connectivityType = request.getConnectivityScope();
+        switch (connectivityType) {
+            case CONNECTIVITY_SCOPE_GLOBAL:
+                callback.awaitInterfaceTethered();
+                break;
+            case CONNECTIVITY_SCOPE_LOCAL:
+                callback.awaitInterfaceLocalOnly();
+                break;
+            default:
+                fail("Unexpected connectivity type requested: " + connectivityType);
+        }
+
+        return callback;
+    }
+
+    protected MyTetheringEventCallback enableEthernetTethering(String iface,
+            Network expectedUpstream) throws Exception {
+        return enableEthernetTethering(iface,
+                new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setShouldShowEntitlementUi(false).build(), expectedUpstream);
+    }
+
+    protected int getMTU(TestNetworkInterface iface) throws SocketException {
+        NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
+        assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
+        return nif.getMTU();
+    }
+
+    protected TapPacketReader makePacketReader(final TestNetworkInterface iface) throws Exception {
+        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
+        return makePacketReader(fd, getMTU(iface));
+    }
+
+    protected TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
+        final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
+        mHandler.post(() -> reader.start());
+        HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
+        return reader;
+    }
+
+    protected static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
+        private final Handler mHandler;
+        private final EthernetManager mEm;
+
+        private TetheredInterfaceRequest mRequest;
+        private final CompletableFuture<String> mFuture = new CompletableFuture<>();
+
+        TetheredInterfaceRequester(Handler handler, EthernetManager em) {
+            mHandler = handler;
+            mEm = em;
+        }
+
+        @Override
+        public void onAvailable(String iface) {
+            Log.d(TAG, "Ethernet interface available: " + iface);
+            mFuture.complete(iface);
+        }
+
+        @Override
+        public void onUnavailable() {
+            mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
+        }
+
+        public CompletableFuture<String> requestInterface() {
+            assertNull("BUG: more than one tethered interface request", mRequest);
+            Log.d(TAG, "Requesting tethered interface");
+            mRequest = runAsShell(NETWORK_SETTINGS, () ->
+                    mEm.requestTetheredInterface(mHandler::post, this));
+            return mFuture;
+        }
+
+        public String getInterface(int timeout) throws Exception {
+            return requestInterface().get(timeout, TimeUnit.MILLISECONDS);
+        }
+
+        public String getInterface() throws Exception {
+            return getInterface(TIMEOUT_MS);
+        }
+
+        public void release() {
+            if (mRequest != null) {
+                mFuture.obtrudeException(new IllegalStateException("Request already released"));
+                mRequest.release();
+                mRequest = null;
+            }
+        }
+    }
+
+    protected TestNetworkInterface createTestInterface() throws Exception {
+        TestNetworkManager tnm = runAsShell(MANAGE_TEST_NETWORKS, () ->
+                mContext.getSystemService(TestNetworkManager.class));
+        TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () ->
+                tnm.createTapInterface());
+        Log.d(TAG, "Created test interface " + iface.getInterfaceName());
+        return iface;
+    }
+
+    protected TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses,
+            final List<InetAddress> dnses) throws Exception {
+        setPreferTestNetworks(true);
+
+        final LinkProperties lp = new LinkProperties();
+        lp.setLinkAddresses(addresses);
+        lp.setDnsServers(dnses);
+        lp.setNat64Prefix(TEST_NAT64PREFIX);
+
+        return runAsShell(MANAGE_TEST_NETWORKS, () -> initTestNetwork(mContext, lp, TIMEOUT_MS));
+    }
+
+    private short getEthType(@NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp) {
+        return isAddressIpv4(srcIp, dstIp) ? (short) ETHER_TYPE_IPV4 : (short) ETHER_TYPE_IPV6;
+    }
+
+    private int getIpProto(@NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp) {
+        return isAddressIpv4(srcIp, dstIp) ? IPPROTO_IP : IPPROTO_IPV6;
+    }
+
+    @NonNull
+    protected ByteBuffer buildUdpPacket(
+            @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+            @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
+            short srcPort, short dstPort, @Nullable final ByteBuffer payload)
+            throws Exception {
+        final int ipProto = getIpProto(srcIp, dstIp);
+        final boolean hasEther = (srcMac != null && dstMac != null);
+        final int payloadLen = (payload == null) ? 0 : payload.limit();
+        final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_UDP,
+                payloadLen);
+        final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+        // [1] Ethernet header
+        if (hasEther) {
+            packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
+        }
+
+        // [2] IP header
+        if (ipProto == IPPROTO_IP) {
+            packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
+                    TIME_TO_LIVE, (byte) IPPROTO_UDP, (Inet4Address) srcIp, (Inet4Address) dstIp);
+        } else {
+            packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_UDP,
+                    HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
+        }
+
+        // [3] UDP header
+        packetBuilder.writeUdpHeader(srcPort, dstPort);
+
+        // [4] Payload
+        if (payload != null) {
+            buffer.put(payload);
+            // in case data might be reused by caller, restore the position and
+            // limit of bytebuffer.
+            payload.clear();
+        }
+
+        return packetBuilder.finalizePacket();
+    }
+
+    @NonNull
+    protected ByteBuffer buildUdpPacket(@NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, short srcPort, short dstPort,
+            @Nullable final ByteBuffer payload) throws Exception {
+        return buildUdpPacket(null /* srcMac */, null /* dstMac */, srcIp, dstIp, srcPort,
+                dstPort, payload);
+    }
+
+    private boolean isAddressIpv4(@NonNull final  InetAddress srcIp,
+            @NonNull final InetAddress dstIp) {
+        if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
+        if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
+
+        fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
+        return false;  // unreachable
+    }
+
+    protected void sendDownloadPacketUdp(@NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
+            boolean is6To4) throws Exception {
+        if (is6To4) {
+            assertFalse("CLAT download test must sends IPv6 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received UDP packet IP protocol. While testing CLAT (is6To4 = true), the packet
+        // on downstream must be IPv4. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is6To4 ? true : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildUdpPacket(srcIp, dstIp, REMOTE_PORT /* srcPort */,
+                LOCAL_PORT /* dstPort */, RX_PAYLOAD);
+        tester.verifyDownload(testPacket, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+            return isExpectedUdpPacket(p, true /* hasEther */, isIpv4, RX_PAYLOAD);
+        });
+    }
+
+    protected void sendUploadPacketUdp(@NonNull final MacAddress srcMac,
+            @NonNull final MacAddress dstMac, @NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
+            boolean is4To6) throws Exception {
+        if (is4To6) {
+            assertTrue("CLAT upload test must sends IPv4 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received UDP packet IP protocol. While testing CLAT (is4To6 = true), the packet
+        // on upstream must be IPv6. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is4To6 ? false : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp,
+                LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */, TX_PAYLOAD);
+        tester.verifyUpload(testPacket, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+            return isExpectedUdpPacket(p, false /* hasEther */, isIpv4, TX_PAYLOAD);
+        });
+    }
+
+
+    @NonNull
+    private ByteBuffer buildTcpPacket(
+            @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+            @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
+            short srcPort, short dstPort, final short seq, final short ack,
+            final byte tcpFlags, @NonNull final ByteBuffer payload) throws Exception {
+        final int ipProto = getIpProto(srcIp, dstIp);
+        final boolean hasEther = (srcMac != null && dstMac != null);
+        final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_TCP,
+                payload.limit());
+        final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+        // [1] Ethernet header
+        if (hasEther) {
+            packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
+        }
+
+        // [2] IP header
+        if (ipProto == IPPROTO_IP) {
+            packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
+                    TIME_TO_LIVE, (byte) IPPROTO_TCP, (Inet4Address) srcIp, (Inet4Address) dstIp);
+        } else {
+            packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_TCP,
+                    HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
+        }
+
+        // [3] TCP header
+        packetBuilder.writeTcpHeader(srcPort, dstPort, seq, ack, tcpFlags, WINDOW, URGENT_POINTER);
+
+        // [4] Payload
+        buffer.put(payload);
+        // in case data might be reused by caller, restore the position and
+        // limit of bytebuffer.
+        payload.clear();
+
+        return packetBuilder.finalizePacket();
+    }
+
+    protected void sendDownloadPacketTcp(@NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, short seq, short ack, byte tcpFlags,
+            @NonNull final ByteBuffer payload, @NonNull final TetheringTester tester,
+            boolean is6To4) throws Exception {
+        if (is6To4) {
+            assertFalse("CLAT download test must sends IPv6 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received TCP packet IP protocol. While testing CLAT (is6To4 = true), the packet
+        // on downstream must be IPv4. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is6To4 ? true : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildTcpPacket(null /* srcMac */, null /* dstMac */,
+                srcIp, dstIp, REMOTE_PORT /* srcPort */, LOCAL_PORT /* dstPort */, seq, ack,
+                tcpFlags, payload);
+        tester.verifyDownload(testPacket, p -> {
+            Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+
+            return isExpectedTcpPacket(p, true /* hasEther */, isIpv4, seq, payload);
+        });
+    }
+
+    protected void sendUploadPacketTcp(@NonNull final MacAddress srcMac,
+            @NonNull final MacAddress dstMac, @NonNull final InetAddress srcIp,
+            @NonNull final InetAddress dstIp, short seq, short ack, byte tcpFlags,
+            @NonNull final ByteBuffer payload, @NonNull final TetheringTester tester,
+            boolean is4To6) throws Exception {
+        if (is4To6) {
+            assertTrue("CLAT upload test must sends IPv4 packet", isAddressIpv4(srcIp, dstIp));
+        }
+
+        // Expected received TCP packet IP protocol. While testing CLAT (is4To6 = true), the packet
+        // on upstream must be IPv6. Otherwise, the IP protocol of test packet is the same on
+        // both downstream and upstream.
+        final boolean isIpv4 = is4To6 ? false : isAddressIpv4(srcIp, dstIp);
+
+        final ByteBuffer testPacket = buildTcpPacket(srcMac, dstMac, srcIp, dstIp,
+                LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */, seq, ack, tcpFlags,
+                payload);
+        tester.verifyUpload(testPacket, p -> {
+            Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+
+            return isExpectedTcpPacket(p, false /* hasEther */, isIpv4, seq, payload);
+        });
+    }
+
+    protected void runTcpTest(
+            @NonNull final MacAddress uploadSrcMac, @NonNull final MacAddress uploadDstMac,
+            @NonNull final InetAddress uploadSrcIp, @NonNull final InetAddress uploadDstIp,
+            @NonNull final InetAddress downloadSrcIp, @NonNull final InetAddress downloadDstIp,
+            @NonNull final TetheringTester tester, boolean isClat) throws Exception {
+        // Three way handshake and data transfer.
+        //
+        // Server (base seq = 2000)                                  Client (base seq = 1000)
+        //   |                                                          |
+        //   |    [1] [SYN] SEQ = 1000                                  |
+        //   |<---------------------------------------------------------|  -
+        //   |                                                          |  ^
+        //   |    [2] [SYN + ACK] SEQ = 2000, ACK = 1000+1              |  |
+        //   |--------------------------------------------------------->|  three way handshake
+        //   |                                                          |  |
+        //   |    [3] [ACK] SEQ = 1001, ACK = 2000+1                    |  v
+        //   |<---------------------------------------------------------|  -
+        //   |                                                          |  ^
+        //   |    [4] [ACK] SEQ = 1001, ACK = 2001, 2 byte payload      |  |
+        //   |<---------------------------------------------------------|  data transfer
+        //   |                                                          |  |
+        //   |    [5] [ACK] SEQ = 2001, ACK = 1001+2, 2 byte payload    |  v
+        //   |--------------------------------------------------------->|  -
+        //   |                                                          |
+        //
+
+        // This test can only verify the packets are transferred end to end but TCP state.
+        // TODO: verify TCP state change via /proc/net/nf_conntrack or netlink conntrack event.
+        // [1] [UPLOAD] [SYN]: SEQ = 1000
+        sendUploadPacketTcp(uploadSrcMac, uploadDstMac, uploadSrcIp, uploadDstIp,
+                (short) 1000 /* seq */, (short) 0 /* ack */, TCPHDR_SYN, EMPTY_PAYLOAD,
+                tester, isClat /* is4To6 */);
+
+        // [2] [DONWLOAD] [SYN + ACK]: SEQ = 2000, ACK = 1001
+        sendDownloadPacketTcp(downloadSrcIp, downloadDstIp, (short) 2000 /* seq */,
+                (short) 1001 /* ack */, (byte) ((TCPHDR_SYN | TCPHDR_ACK) & 0xff), EMPTY_PAYLOAD,
+                tester, isClat /* is6To4 */);
+
+        // [3] [UPLOAD] [ACK]: SEQ = 1001, ACK = 2001
+        sendUploadPacketTcp(uploadSrcMac, uploadDstMac, uploadSrcIp, uploadDstIp,
+                (short) 1001 /* seq */, (short) 2001 /* ack */, TCPHDR_ACK, EMPTY_PAYLOAD, tester,
+                isClat /* is4To6 */);
+
+        // [4] [UPLOAD] [ACK]: SEQ = 1001, ACK = 2001, 2 byte payload
+        sendUploadPacketTcp(uploadSrcMac, uploadDstMac, uploadSrcIp, uploadDstIp,
+                (short) 1001 /* seq */, (short) 2001 /* ack */, TCPHDR_ACK, TX_PAYLOAD,
+                tester, isClat /* is4To6 */);
+
+        // [5] [DONWLOAD] [ACK]: SEQ = 2001, ACK = 1003, 2 byte payload
+        sendDownloadPacketTcp(downloadSrcIp, downloadDstIp, (short) 2001 /* seq */,
+                (short) 1003 /* ack */, TCPHDR_ACK, RX_PAYLOAD, tester, isClat /* is6To4 */);
+
+        // TODO: test BPF offload maps.
+    }
+
+    // TODO: remove ipv4 verification (is4To6 = false) once upstream connected notification race is
+    // fixed. See #runUdp4Test.
+    //
+    // This function sends a probe packet to downstream interface and exam the result from upstream
+    // interface to make sure ipv4 tethering is ready. Return the entire packet which received from
+    // upstream interface.
+    @NonNull
+    protected byte[] probeV4TetheringConnectivity(TetheringTester tester, TetheredDevice tethered,
+            boolean is4To6) throws Exception {
+        final ByteBuffer probePacket = buildUdpPacket(tethered.macAddr,
+                tethered.routerMacAddr, tethered.ipv4Addr /* srcIp */,
+                REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /* dstPort */,
+                TEST_REACHABILITY_PAYLOAD);
+
+        // Send a UDP packet from client and check the packet can be found on upstream interface.
+        for (int i = 0; i < TETHER_REACHABILITY_ATTEMPTS; i++) {
+            byte[] expectedPacket = tester.testUpload(probePacket, p -> {
+                Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+                // If is4To6 is true, the ipv4 probe packet would be translated to ipv6 by Clat and
+                // would see this translated ipv6 packet in upstream interface.
+                return isExpectedUdpPacket(p, false /* hasEther */, !is4To6 /* isIpv4 */,
+                        TEST_REACHABILITY_PAYLOAD);
+            });
+            if (expectedPacket != null) return expectedPacket;
+        }
+
+        fail("Can't verify " + (is4To6 ? "ipv4 to ipv6" : "ipv4") + " tethering connectivity after "
+                + TETHER_REACHABILITY_ATTEMPTS + " attempts");
+        return null;
+    }
+
+    // TODO: remove triggering upstream reselection once test network can replace selected upstream
+    // network in Tethering module.
+    private void maybeRetryTestedUpstreamChanged(final Network expectedUpstream,
+            final TimeoutException fallbackException) throws Exception {
+        // Fall back original exception because no way to reselect if there is no WIFI feature.
+        assertTrue(fallbackException.toString(), mPackageManager.hasSystemFeature(FEATURE_WIFI));
+
+        // Try to toggle wifi network, if any, to reselect upstream network via default network
+        // switching. Because test network has higher priority than internet network, this can
+        // help selecting test network to be upstream network for testing. This tries to avoid
+        // the flaky upstream selection under multinetwork environment. Internet and test network
+        // upstream changed event order is not guaranteed. Once tethering selects non-test
+        // upstream {wifi, ..}, test network won't be selected anymore. If too many test cases
+        // trigger the reselection, the total test time may over test suite 1 minmute timeout.
+        // Probably need to disable/restore all internet networks in a common place of test
+        // process. Currently, EthernetTetheringTest is part of CTS test which needs wifi network
+        // connection if device has wifi feature. CtsNetUtils#toggleWifi() checks wifi connection
+        // during the toggling process.
+        // See Tethering#chooseUpstreamType, CtsNetUtils#toggleWifi.
+        // TODO: toggle cellular network if the device has no WIFI feature.
+        Log.d(TAG, "Toggle WIFI to retry upstream selection");
+        mCtsNetUtils.toggleWifi();
+
+        // Wait for expected upstream.
+        final CompletableFuture<Network> future = new CompletableFuture<>();
+        final TetheringEventCallback callback = new TetheringEventCallback() {
+            @Override
+            public void onUpstreamChanged(Network network) {
+                Log.d(TAG, "Got upstream changed: " + network);
+                if (Objects.equals(expectedUpstream, network)) {
+                    future.complete(network);
+                }
+            }
+        };
+        try {
+            mTm.registerTetheringEventCallback(mHandler::post, callback);
+            assertEquals("onUpstreamChanged for unexpected network", expectedUpstream,
+                    future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } catch (TimeoutException e) {
+            throw new AssertionError("Did not receive upstream " + expectedUpstream
+                    + " callback after " + TIMEOUT_MS + "ms");
+        } finally {
+            mTm.unregisterTetheringEventCallback(callback);
+        }
+    }
+
+    protected TetheringTester initTetheringTester(List<LinkAddress> upstreamAddresses,
+            List<InetAddress> upstreamDnses) throws Exception {
+        assumeFalse(isInterfaceForTetheringAvailable());
+
+        // MyTetheringEventCallback currently only support await first available upstream. Tethering
+        // may select internet network as upstream if test network is not available and not be
+        // preferred yet. Create test upstream network before enable tethering.
+        mUpstreamTracker = createTestUpstream(upstreamAddresses, upstreamDnses);
+
+        mDownstreamIface = createTestInterface();
+        setIncludeTestInterfaces(true);
+
+        // Make sure EtherentTracker use "mDownstreamIface" as server mode interface.
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mDownstreamIface.getInterfaceName(), mTetheredInterfaceRequester.getInterface());
+
+        mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(),
+                mUpstreamTracker.getNetwork());
+
+        try {
+            assertEquals("onUpstreamChanged for test network", mUpstreamTracker.getNetwork(),
+                    mTetheringEventCallback.awaitUpstreamChanged(
+                            true /* throwTimeoutException */));
+        } catch (TimeoutException e) {
+            // Due to race condition inside tethering module, test network may not be selected as
+            // tethering upstream. Force tethering retry upstream if possible. If it is not
+            // possible to retry, fail the test with the original timeout exception.
+            maybeRetryTestedUpstreamChanged(mUpstreamTracker.getNetwork(), e);
+        }
+
+        mDownstreamReader = makePacketReader(mDownstreamIface);
+        mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());
+
+        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+        // Currently tethering don't have API to tell when ipv6 tethering is available. Thus, make
+        // sure tethering already have ipv6 connectivity before testing.
+        if (cm.getLinkProperties(mUpstreamTracker.getNetwork()).hasGlobalIpv6Address()) {
+            waitForRouterAdvertisement(mDownstreamReader, mDownstreamIface.getInterfaceName(),
+                    WAIT_RA_TIMEOUT_MS);
+        }
+
+        return new TetheringTester(mDownstreamReader, mUpstreamReader);
+    }
+
+    protected <T> List<T> toList(T... array) {
+        return Arrays.asList(array);
+    }
+}
diff --git a/Tethering/tests/integration/src/android/net/TetheringTester.java b/Tethering/tests/integration/src/android/net/TetheringTester.java
index 9cc2e49..ae39b24 100644
--- a/Tethering/tests/integration/src/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/src/android/net/TetheringTester.java
@@ -17,7 +17,9 @@
 package android.net;
 
 import static android.net.InetAddresses.parseNumericAddress;
+import static android.system.OsConstants.IPPROTO_ICMP;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 
 import static com.android.net.module.util.DnsPacket.ANSECTION;
@@ -39,6 +41,7 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
+import static com.android.net.module.util.NetworkStackConstants.TCPHDR_SYN;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -57,6 +60,7 @@
 import com.android.net.module.util.Ipv6Utils;
 import com.android.net.module.util.Struct;
 import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.Icmpv4Header;
 import com.android.net.module.util.structs.Icmpv6Header;
 import com.android.net.module.util.structs.Ipv4Header;
 import com.android.net.module.util.structs.Ipv6Header;
@@ -64,6 +68,7 @@
 import com.android.net.module.util.structs.NsHeader;
 import com.android.net.module.util.structs.PrefixInformationOption;
 import com.android.net.module.util.structs.RaHeader;
+import com.android.net.module.util.structs.TcpHeader;
 import com.android.net.module.util.structs.UdpHeader;
 import com.android.networkstack.arp.ArpPacket;
 import com.android.testutils.TapPacketReader;
@@ -268,7 +273,8 @@
 
     private List<PrefixInformationOption> getRaPrefixOptions(byte[] packet) {
         ByteBuffer buf = ByteBuffer.wrap(packet);
-        if (!isExpectedIcmpv6Packet(buf, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) {
+        if (!isExpectedIcmpPacket(buf, true /* hasEth */, false /* isIpv4 */,
+                ICMPV6_ROUTER_ADVERTISEMENT)) {
             fail("Parsing RA packet fail");
         }
 
@@ -298,7 +304,8 @@
         sendRsPacket(srcMac, dstMac);
 
         final byte[] raPacket = verifyPacketNotNull("Receive RA fail", getDownloadPacket(p -> {
-            return isExpectedIcmpv6Packet(p, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT);
+            return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */,
+                    ICMPV6_ROUTER_ADVERTISEMENT);
         }));
 
         final List<PrefixInformationOption> options = getRaPrefixOptions(raPacket);
@@ -360,20 +367,27 @@
         }
     }
 
-    public static boolean isExpectedIcmpv6Packet(byte[] packet, boolean hasEth, int type) {
+    public static boolean isExpectedIcmpPacket(byte[] packet, boolean hasEth, boolean isIpv4,
+            int type) {
         final ByteBuffer buf = ByteBuffer.wrap(packet);
-        return isExpectedIcmpv6Packet(buf, hasEth, type);
+        return isExpectedIcmpPacket(buf, hasEth, isIpv4, type);
     }
 
-    private static boolean isExpectedIcmpv6Packet(ByteBuffer buf, boolean hasEth, int type) {
+    private static boolean isExpectedIcmpPacket(ByteBuffer buf, boolean hasEth, boolean isIpv4,
+            int type) {
         try {
-            if (hasEth && !hasExpectedEtherHeader(buf, false /* isIpv4 */)) return false;
+            if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
 
-            if (!hasExpectedIpHeader(buf, false /* isIpv4 */, IPPROTO_ICMPV6)) return false;
+            final int ipProto = isIpv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6;
+            if (!hasExpectedIpHeader(buf, isIpv4, ipProto)) return false;
 
-            return Struct.parse(Icmpv6Header.class, buf).type == (short) type;
+            if (isIpv4) {
+                return Struct.parse(Icmpv4Header.class, buf).type == (short) type;
+            } else {
+                return Struct.parse(Icmpv6Header.class, buf).type == (short) type;
+            }
         } catch (Exception e) {
-            // Parsing packet fail means it is not icmpv6 packet.
+            // Parsing packet fail means it is not icmp packet.
         }
 
         return false;
@@ -578,6 +592,42 @@
         return true;
     }
 
+
+    private static boolean isTcpSynPacket(@NonNull final TcpHeader tcpHeader) {
+        return (tcpHeader.dataOffsetAndControlBits & TCPHDR_SYN) != 0;
+    }
+
+    public static boolean isExpectedTcpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
+            boolean isIpv4, int seq, @NonNull final ByteBuffer payload) {
+        final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
+        try {
+            if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
+
+            if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_TCP)) return false;
+
+            final TcpHeader tcpHeader = Struct.parse(TcpHeader.class, buf);
+            if (tcpHeader.seq != seq) return false;
+
+            // Don't try to parse the payload if it is a TCP SYN segment because additional TCP
+            // option MSS may be added in the SYN segment. Currently, TetherController uses
+            // iptables to limit downstream MSS for IPv4. The additional TCP options will be
+            // misunderstood as payload because parsing TCP options are not supported by class
+            // TcpHeader for now. See TetherController::setupIptablesHooks.
+            // TODO: remove once TcpHeader supports parsing TCP options.
+            if (isTcpSynPacket(tcpHeader)) {
+                Log.d(TAG, "Found SYN segment. Ignore parsing the remaining part of packet.");
+                return true;
+            }
+
+            if (payload.limit() != buf.remaining()) return false;
+            return Arrays.equals(getRemaining(buf), getRemaining(payload.asReadOnlyBuffer()));
+        } catch (Exception e) {
+            // Parsing packet fail means it is not tcp packet.
+        }
+
+        return false;
+    }
+
     private void sendUploadPacket(ByteBuffer packet) throws Exception {
         mDownstreamReader.sendResponse(packet);
     }
diff --git a/Tethering/tests/mts/Android.bp b/Tethering/tests/mts/Android.bp
index a84fdd2..ae36499 100644
--- a/Tethering/tests/mts/Android.bp
+++ b/Tethering/tests/mts/Android.bp
@@ -41,6 +41,8 @@
         "ctstestrunner-axt",
         "junit",
         "junit-params",
+        "connectivity-net-module-utils-bpf",
+        "net-utils-device-common-bpf",
     ],
 
     jni_libs: [
diff --git a/Tethering/tests/mts/src/android/tethering/mts/SkDestroyListenerTest.java b/Tethering/tests/mts/src/android/tethering/mts/SkDestroyListenerTest.java
new file mode 100644
index 0000000..9494aa4
--- /dev/null
+++ b/Tethering/tests/mts/src/android/tethering/mts/SkDestroyListenerTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 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 android.tethering.mts;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.TrafficStats;
+import android.os.Build;
+import android.os.Process;
+import android.system.Os;
+import android.util.Pair;
+
+import com.android.net.module.util.BpfDump;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
+public class SkDestroyListenerTest {
+    private static final int COOKIE_TAG = 0x1234abcd;
+    private static final int SOCKET_COUNT = 100;
+    private static final int SOCKET_CLOSE_WAIT_MS = 200;
+    private static final String LINE_DELIMITER = "\\n";
+    private static final String DUMP_COMMAND = "dumpsys netstats --bpfRawMap --cookieTagMap";
+
+    private Map<CookieTagMapKey, CookieTagMapValue> parseBpfRawMap(final String dump) {
+        final Map<CookieTagMapKey, CookieTagMapValue> map = new HashMap<>();
+        for (final String line: dump.split(LINE_DELIMITER)) {
+            final Pair<CookieTagMapKey, CookieTagMapValue> keyValue =
+                    BpfDump.fromBase64EncodedString(CookieTagMapKey.class,
+                            CookieTagMapValue.class, line.trim());
+            map.put(keyValue.first, keyValue.second);
+        }
+        return map;
+    }
+
+    private int countTaggedSocket() {
+        final String dump = runShellCommandOrThrow(DUMP_COMMAND);
+        final Map<CookieTagMapKey, CookieTagMapValue> cookieTagMap = parseBpfRawMap(dump);
+        int count = 0;
+        for (final CookieTagMapValue value: cookieTagMap.values()) {
+            if (value.tag == COOKIE_TAG && value.uid == Process.myUid()) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private boolean noTaggedSocket() {
+        return countTaggedSocket() == 0;
+    }
+
+    private void doTestSkDestroyListener(final int family, final int type) throws Exception {
+        assertTrue("There are tagged sockets before test", noTaggedSocket());
+
+        TrafficStats.setThreadStatsTag(COOKIE_TAG);
+        final List<FileDescriptor> fds = new ArrayList<>();
+        for (int i = 0; i < SOCKET_COUNT; i++) {
+            fds.add(Os.socket(family, type, 0 /* protocol */));
+        }
+        TrafficStats.clearThreadStatsTag();
+        assertEquals("Number of tagged socket does not match after creating sockets",
+                SOCKET_COUNT, countTaggedSocket());
+
+        for (final FileDescriptor fd: fds) {
+            Os.close(fd);
+        }
+        // Wait a bit for skDestroyListener to handle all the netlink messages.
+        Thread.sleep(SOCKET_CLOSE_WAIT_MS);
+        assertTrue("There are tagged sockets after closing sockets", noTaggedSocket());
+    }
+
+    @Test
+    public void testSkDestroyListener() throws Exception {
+        doTestSkDestroyListener(AF_INET, SOCK_STREAM);
+        doTestSkDestroyListener(AF_INET, SOCK_DGRAM);
+        doTestSkDestroyListener(AF_INET6, SOCK_STREAM);
+        doTestSkDestroyListener(AF_INET6, SOCK_DGRAM);
+    }
+}
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
index d38a7c3..23fb60c 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -16,28 +16,32 @@
 
 package com.android.networkstack.tethering;
 
+import static android.system.OsConstants.EAGAIN;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.NETLINK_NETFILTER;
+
 import static com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_DESTROY;
 import static com.android.networkstack.tethering.OffloadHardwareInterface.NF_NETLINK_CONNTRACK_NEW;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.NativeHandle;
-import android.system.Os;
+import android.system.ErrnoException;
+import android.util.Log;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.netlink.ConntrackMessage;
+import com.android.net.module.util.netlink.NetlinkMessage;
+import com.android.net.module.util.netlink.NetlinkSocket;
 import com.android.net.module.util.netlink.StructNlMsgHdr;
 
 import org.junit.Before;
@@ -45,18 +49,18 @@
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
+import java.io.FileDescriptor;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketAddress;
 import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ConntrackSocketTest {
     private static final long TIMEOUT = 500;
+    private static final String TAG = ConntrackSocketTest.class.getSimpleName();
 
     private HandlerThread mHandlerThread;
     private Handler mHandler;
@@ -80,51 +84,72 @@
         mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps);
     }
 
+    void findConnectionOrThrow(FileDescriptor fd, InetSocketAddress local, InetSocketAddress remote)
+            throws Exception {
+        Log.d(TAG, "Looking for socket " + local + " -> " + remote);
+
+        // Loop until the socket is found (and return) or recvMessage throws an exception.
+        while (true) {
+            final ByteBuffer buffer = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT);
+
+            // Parse all the netlink messages in the dump.
+            // NetlinkMessage#parse returns null if the message is truncated or invalid.
+            while (buffer.remaining() > 0) {
+                NetlinkMessage nlmsg = NetlinkMessage.parse(buffer, NETLINK_NETFILTER);
+                Log.d(TAG, "Got netlink message: " + nlmsg);
+                if (!(nlmsg instanceof ConntrackMessage)) {
+                    continue;
+                }
+
+                StructNlMsgHdr nlmsghdr = nlmsg.getHeader();
+                ConntrackMessage ctmsg = (ConntrackMessage) nlmsg;
+                ConntrackMessage.Tuple tuple = ctmsg.tupleOrig;
+
+                if (nlmsghdr.nlmsg_type == (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW)
+                        && tuple.protoNum == IPPROTO_TCP
+                        && tuple.srcIp.equals(local.getAddress())
+                        && tuple.dstIp.equals(remote.getAddress())
+                        && tuple.srcPort == (short) local.getPort()
+                        && tuple.dstPort == (short) remote.getPort()) {
+                    return;
+                }
+            }
+        }
+    }
+
     @Test
     public void testIpv4ConntrackSocket() throws Exception {
         // Set up server and connect.
-        final InetSocketAddress anyAddress = new InetSocketAddress(
-                InetAddress.getByName("127.0.0.1"), 0);
+        final InetAddress localhost = InetAddress.getByName("127.0.0.1");
+        final InetSocketAddress anyAddress = new InetSocketAddress(localhost, 0);
         final ServerSocket serverSocket = new ServerSocket();
         serverSocket.bind(anyAddress);
-        final SocketAddress theAddress = serverSocket.getLocalSocketAddress();
+        final InetSocketAddress theAddress =
+                (InetSocketAddress) serverSocket.getLocalSocketAddress();
 
         // Make a connection to the server.
         final Socket socket = new Socket();
         socket.connect(theAddress);
+        final InetSocketAddress localAddress = (InetSocketAddress) socket.getLocalSocketAddress();
         final Socket acceptedSocket = serverSocket.accept();
 
         final NativeHandle handle = mDeps.createConntrackSocket(
                 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
-        mOffloadHw.sendIpv4NfGenMsg(handle,
-                (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET),
-                (short) (NLM_F_REQUEST | NLM_F_DUMP));
-
-        boolean foundConntrackEntry = false;
-        ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_RECV_BUFSIZE);
-        buffer.order(ByteOrder.nativeOrder());
+        mOffloadHw.requestSocketDump(handle);
 
         try {
-            while (Os.read(handle.getFileDescriptor(), buffer) > 0) {
-                buffer.flip();
-
-                // TODO: ConntrackMessage should get a parse API like StructNlMsgHdr
-                // so we can confirm that the conntrack added is for the TCP connection above.
-                final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(buffer);
-                assertNotNull(nlmsghdr);
-
-                // As long as 1 conntrack entry is found test case will pass, even if it's not
-                // the from the TCP connection above.
-                if (nlmsghdr.nlmsg_type == ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW)) {
-                    foundConntrackEntry = true;
-                    break;
-                }
+            findConnectionOrThrow(handle.getFileDescriptor(), localAddress, theAddress);
+            // No exceptions? Socket was found, test passes.
+        } catch (ErrnoException e) {
+            if (e.errno == EAGAIN) {
+                fail("Did not find socket " + localAddress + "->" + theAddress + " in dump");
+            } else {
+                throw e;
             }
         } finally {
             socket.close();
             serverSocket.close();
+            acceptedSocket.close();
         }
-        assertTrue("Did not receive any NFNL_SUBSYS_CTNETLINK/IPCTNL_MSG_CT_NEW message",
-                foundConntrackEntry);
     }
 }
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index fd1166c..d78fbfd 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -68,6 +68,7 @@
         "framework-minus-apex",
         "framework-res",
         "framework-bluetooth.stubs.module_lib",
+        "framework-configinfrastructure",
         "framework-connectivity.impl",
         "framework-connectivity-t.impl",
         "framework-tethering.impl",
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f242227..f0d9057 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -53,6 +53,7 @@
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -99,6 +100,7 @@
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.bpf.TetherStatsKey;
@@ -108,6 +110,7 @@
 import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
 import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
 import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
 import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
 import com.android.networkstack.tethering.PrivateAddressCoordinator;
 import com.android.networkstack.tethering.Tether6Value;
@@ -197,6 +200,7 @@
     @Mock private BpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap;
     @Mock private BpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
     @Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
+    @Mock private BpfMap<S32, S32> mBpfErrorMap;
 
     @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
 
@@ -271,6 +275,7 @@
                 mTestAddress);
     }
 
+    @SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run()
     private void setUpDhcpServer() throws Exception {
         doAnswer(inv -> {
             final IDhcpServerCallbacks cb = inv.getArgument(2);
@@ -359,6 +364,11 @@
                     public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
                         return mBpfDevMap;
                     }
+
+                    @Nullable
+                    public BpfMap<S32, S32> getBpfErrorMap() {
+                        return mBpfErrorMap;
+                    }
                 };
         mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
 
@@ -1519,4 +1529,56 @@
         verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(
                 mIpServer, makeForwardingRule(IPSEC_IFINDEX, neigh, mac));
     }
+
+    // TODO: move to BpfCoordinatorTest once IpNeighborMonitor is migrated to BpfCoordinator.
+    @Test
+    public void addRemoveTetherClient() throws Exception {
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+                DEFAULT_USING_BPF_OFFLOAD);
+
+        final int myIfindex = TEST_IFACE_PARAMS.index;
+        final int notMyIfindex = myIfindex - 1;
+
+        final InetAddress neighA = InetAddresses.parseNumericAddress("192.168.80.1");
+        final InetAddress neighB = InetAddresses.parseNumericAddress("192.168.80.2");
+        final InetAddress neighLL = InetAddresses.parseNumericAddress("169.254.0.1");
+        final InetAddress neighMC = InetAddresses.parseNumericAddress("224.0.0.1");
+        final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
+        final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+        final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
+
+        // Events on other interfaces are ignored.
+        recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mBpfCoordinator);
+
+        // Events on this interface are received and sent to BpfCoordinator.
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
+                TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macA));
+        clearInvocations(mBpfCoordinator);
+
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mBpfCoordinator).tetherOffloadClientAdd(mIpServer, new ClientInfo(myIfindex,
+                TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macB));
+        clearInvocations(mBpfCoordinator);
+
+        // Link-local and multicast neighbors are ignored.
+        recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mBpfCoordinator);
+        recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mBpfCoordinator);
+        clearInvocations(mBpfCoordinator);
+
+        // A neighbor that is no longer valid causes the client to be removed.
+        // NUD_FAILED events do not have a MAC address.
+        recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
+        verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer,  new ClientInfo(myIfindex,
+                TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighA, macNull));
+        clearInvocations(mBpfCoordinator);
+
+        // A neighbor that is deleted causes the client to be removed.
+        recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+        verify(mBpfCoordinator).tetherOffloadClientRemove(mIpServer, new ClientInfo(myIfindex,
+                TEST_IFACE_PARAMS.macAddr, (Inet4Address) neighB, macNull));
+    }
 }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 758b533..225fed7 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -94,11 +94,13 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.bpf.TetherStatsKey;
@@ -128,6 +130,7 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 
+import java.io.StringWriter;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -135,6 +138,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.Map;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -192,13 +196,11 @@
             UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.fromString("44:55:66:00:00:0c"),
             NetworkStackConstants.ETHER_MTU);
 
-    private static final HashMap<Integer, UpstreamInformation> UPSTREAM_INFORMATIONS =
-            new HashMap<Integer, UpstreamInformation>() {{
-                    put(UPSTREAM_IFINDEX, new UpstreamInformation(UPSTREAM_IFACE_PARAMS,
-                            PUBLIC_ADDR, NetworkCapabilities.TRANSPORT_CELLULAR, TEST_NET_ID));
-                    put(UPSTREAM_IFINDEX2, new UpstreamInformation(UPSTREAM_IFACE_PARAMS2,
-                            PUBLIC_ADDR2, NetworkCapabilities.TRANSPORT_WIFI, TEST_NET_ID2));
-            }};
+    private static final Map<Integer, UpstreamInformation> UPSTREAM_INFORMATIONS = Map.of(
+            UPSTREAM_IFINDEX, new UpstreamInformation(UPSTREAM_IFACE_PARAMS,
+                    PUBLIC_ADDR, NetworkCapabilities.TRANSPORT_CELLULAR, TEST_NET_ID),
+            UPSTREAM_IFINDEX2, new UpstreamInformation(UPSTREAM_IFACE_PARAMS2,
+                    PUBLIC_ADDR2, NetworkCapabilities.TRANSPORT_WIFI, TEST_NET_ID2));
 
     private static final ClientInfo CLIENT_INFO_A = new ClientInfo(DOWNSTREAM_IFINDEX,
             DOWNSTREAM_MAC, PRIVATE_ADDR, MAC_A);
@@ -362,9 +364,6 @@
     @Mock private IpServer mIpServer2;
     @Mock private TetheringConfiguration mTetherConfig;
     @Mock private ConntrackMonitor mConntrackMonitor;
-    @Mock private IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
-    @Mock private IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map;
-    @Mock private IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
 
     // Late init since methods must be called by the thread that created this object.
     private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
@@ -383,10 +382,18 @@
             spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
     private final IBpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map =
             spy(new TestBpfMap<>(Tether4Key.class, Tether4Value.class));
+    private final IBpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map =
+            spy(new TestBpfMap<>(TetherDownstream6Key.class, Tether6Value.class));
+    private final IBpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map =
+            spy(new TestBpfMap<>(TetherUpstream6Key.class, Tether6Value.class));
     private final IBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
             spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
     private final IBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
             spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class));
+    private final IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap =
+            spy(new TestBpfMap<>(TetherDevKey.class, TetherDevValue.class));
+    private final IBpfMap<S32, S32> mBpfErrorMap =
+            spy(new TestBpfMap<>(S32.class, S32.class));
     private BpfCoordinator.Dependencies mDeps =
             spy(new BpfCoordinator.Dependencies() {
                     @NonNull
@@ -457,6 +464,11 @@
                     public IBpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
                         return mBpfDevMap;
                     }
+
+                    @Nullable
+                    public IBpfMap<S32, S32> getBpfErrorMap() {
+                        return mBpfErrorMap;
+                    }
             });
 
     @Before public void setUp() {
@@ -1413,7 +1425,7 @@
 
         // [1] Don't stop monitoring if it has never started.
         coordinator.stopMonitoring(mIpServer);
-        verify(mConntrackMonitor, never()).start();
+        verify(mConntrackMonitor, never()).stop();
 
         // [2] Start monitoring.
         coordinator.startMonitoring(mIpServer);
@@ -2092,4 +2104,95 @@
         assertNull(mBpfUpstream4Map.getValue(upstream4KeyB));
         assertNull(mBpfDownstream4Map.getValue(downstream4KeyB));
     }
+
+    @Test
+    public void testIpv6ForwardingRuleToString() throws Exception {
+        final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+        assertEquals("upstreamIfindex: 1001, downstreamIfindex: 1003, address: 2001:db8::1, "
+                + "srcMac: 12:34:56:78:90:ab, dstMac: 00:00:00:00:00:0a", rule.toString());
+    }
+
+    private void verifyDump(@NonNull final BpfCoordinator coordinator) {
+        final StringWriter stringWriter = new StringWriter();
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ");
+        coordinator.dump(ipw);
+        assertFalse(stringWriter.toString().isEmpty());
+    }
+
+    @Test
+    public void testDumpDoesNotCrash() throws Exception {
+        // This dump test only used to for improving mainline module test coverage and doesn't
+        // really do any meaningful tests.
+        // TODO: consider verifying the dump content and separate tests into testDumpXXX().
+        final BpfCoordinator coordinator = makeBpfCoordinator();
+
+        // [1] Dump mostly empty content.
+        verifyDump(coordinator);
+
+        // [2] Dump mostly non-empty content.
+        // Test the following dump function and fill the corresponding content to execute
+        // code as more as possible for test coverage.
+        // - dumpBpfForwardingRulesIpv4
+        //   * mBpfDownstream4Map
+        //   * mBpfUpstream4Map
+        // - dumpBpfForwardingRulesIpv6
+        //   * mBpfDownstream6Map
+        //   * mBpfUpstream6Map
+        // - dumpStats
+        //   * mBpfStatsMap
+        // - dumpDevmap
+        //   * mBpfDevMap
+        // - dumpCounters
+        //   * mBpfErrorMap
+        // - dumpIpv6ForwardingRulesByDownstream
+        //   * mIpv6ForwardingRules
+
+        // dumpBpfForwardingRulesIpv4
+        mBpfDownstream4Map.insertEntry(
+                new TestDownstream4Key.Builder().build(),
+                new TestDownstream4Value.Builder().build());
+        mBpfUpstream4Map.insertEntry(
+                new TestUpstream4Key.Builder().build(),
+                new TestUpstream4Value.Builder().build());
+
+        // dumpBpfForwardingRulesIpv6
+        final Ipv6ForwardingRule rule = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A);
+        mBpfDownstream6Map.insertEntry(rule.makeTetherDownstream6Key(), rule.makeTether6Value());
+
+        final TetherUpstream6Key upstream6Key = new TetherUpstream6Key(DOWNSTREAM_IFINDEX,
+                DOWNSTREAM_MAC);
+        final Tether6Value upstream6Value = new Tether6Value(UPSTREAM_IFINDEX,
+                MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS,
+                ETH_P_IPV6, NetworkStackConstants.ETHER_MTU);
+        mBpfUpstream6Map.insertEntry(upstream6Key, upstream6Value);
+
+        // dumpStats
+        mBpfStatsMap.insertEntry(
+                new TetherStatsKey(UPSTREAM_IFINDEX),
+                new TetherStatsValue(
+                        0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */,
+                        0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */));
+
+        // dumpDevmap
+        coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
+        mBpfDevMap.insertEntry(
+                new TetherDevKey(UPSTREAM_IFINDEX),
+                new TetherDevValue(UPSTREAM_IFINDEX));
+
+        // dumpCounters
+        // The error code is defined in packages/modules/Connectivity/bpf_progs/bpf_tethering.h.
+        mBpfErrorMap.insertEntry(
+                new S32(0 /* INVALID_IPV4_VERSION */),
+                new S32(1000 /* count */));
+
+        // dumpIpv6ForwardingRulesByDownstream
+        final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
+                ipv6ForwardingRules = coordinator.getForwardingRulesForTesting();
+        final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> addressRuleMap =
+                new LinkedHashMap<>();
+        addressRuleMap.put(rule.address, rule);
+        ipv6ForwardingRules.put(mIpServer, addressRuleMap);
+
+        verifyDump(coordinator);
+    }
 }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
index 95ec38f..0d686ed 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
@@ -33,7 +33,7 @@
     }
 
     @Override
-    protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
+    protected boolean isFeatureEnabled(Context ctx, String namespace, String featureVersionFlag) {
         return false;
     }
 
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index e0d77ee..b2cbf75 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -19,7 +19,6 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 
 import static com.android.networkstack.apishim.common.ShimUtils.isAtLeastS;
 
@@ -330,28 +329,6 @@
             this.legacyType = toLegacyType(networkCapabilities);
         }
 
-        // Used for test network only because ConnectivityManager.networkCapabilitiesForType
-        // doesn't support "TRANSPORT_TEST -> TYPE_TEST" in #matchesLegacyType. Beware of
-        // satisfiedByNetworkCapabilities doesn't check on new |networkCapabilities| as
-        // #matchesLegacyType.
-        // TODO: refactor when tethering no longer uses CONNECTIVITY_ACTION.
-        private TestNetworkAgent(TestConnectivityManager cm) {
-            this.cm = cm;
-            networkId = new Network(cm.getNetworkId());
-            networkCapabilities = new NetworkCapabilities.Builder()
-                    .addTransportType(TRANSPORT_TEST)
-                    .addCapability(NET_CAPABILITY_INTERNET)
-                    .build();
-            linkProperties = new LinkProperties();
-            legacyType = TYPE_TEST;
-        }
-
-        // TODO: refactor when tethering no longer uses CONNECTIVITY_ACTION.
-        public static TestNetworkAgent buildTestNetworkAgentForTestNetwork(
-                TestConnectivityManager cm) {
-            return new TestNetworkAgent(cm);
-        }
-
         private static int toLegacyType(NetworkCapabilities nc) {
             for (int type = 0; type < ConnectivityManager.TYPE_TEST; type++) {
                 if (matchesLegacyType(nc, type)) return type;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 1a12125..f662c02 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -545,7 +545,8 @@
         assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
     }
 
-    @Test
+    // The config only works on T-
+    @Test @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testChooseUpstreamAutomatically() throws Exception {
         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
                 .thenReturn(true);
@@ -556,6 +557,20 @@
         assertChooseUpstreamAutomaticallyIs(false);
     }
 
+    // The automatic mode is always enabled on U+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    public void testChooseUpstreamAutomaticallyAfterT() throws Exception {
+        // Expect that automatic mode is always enabled no matter what
+        // config_tether_upstream_automatic is.
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
+                .thenReturn(true);
+        assertChooseUpstreamAutomaticallyIs(true);
+
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
+                .thenReturn(false);
+        assertChooseUpstreamAutomaticallyIs(true);
+    }
+
     // The flag override only works on R-
     @Test @IgnoreAfter(Build.VERSION_CODES.R)
     public void testChooseUpstreamAutomatically_FlagOverride() throws Exception {
@@ -574,14 +589,34 @@
         assertChooseUpstreamAutomaticallyIs(false);
     }
 
-    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
-    public void testChooseUpstreamAutomatically_FlagOverrideAfterR() throws Exception {
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R) @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
+    public void testChooseUpstreamAutomatically_FlagOverrideOnSAndT() throws Exception {
         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
                 .thenReturn(false);
         setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
         assertChooseUpstreamAutomaticallyIs(false);
     }
 
+    // The automatic mode is always enabled on U+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    public void testChooseUpstreamAutomatically_FlagOverrideAfterT() throws Exception {
+        // Expect that automatic mode is always enabled no matter what
+        // TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION is.
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
+                .thenReturn(false);
+        setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
+        assertTrue(DeviceConfigUtils.isFeatureEnabled(mMockContext, NAMESPACE_CONNECTIVITY,
+                TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION, APEX_NAME, false));
+
+        assertChooseUpstreamAutomaticallyIs(true);
+
+        setTetherForceUpstreamAutomaticFlagVersion(0L);
+        assertChooseUpstreamAutomaticallyIs(true);
+
+        setTetherForceUpstreamAutomaticFlagVersion(Long.MAX_VALUE);
+        assertChooseUpstreamAutomaticallyIs(true);
+    }
+
     private void setTetherForceUpstreamAutomaticFlagVersion(Long version) {
         doReturn(version == null ? null : Long.toString(version)).when(
                 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index e114cb5..38f1e9c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -664,17 +664,17 @@
             assertEquals("Internal callback is not registered", 1, callbacks.size());
             assertNotNull(weakTm.get());
 
+            // Calling System.gc() or System.runFinalization() doesn't guarantee GCs or finalizers
+            // are executed synchronously. The finalizer is called after GC on a separate thread.
             final int attempts = 100;
             final long waitIntervalMs = 50;
             for (int i = 0; i < attempts; i++) {
                 forceGc();
-                if (weakTm.get() == null) break;
+                if (weakTm.get() == null && callbacks.size() == 0) break;
 
                 Thread.sleep(waitIntervalMs);
             }
-            assertNull("TetheringManager weak reference still not null after " + attempts
-                    + " attempts", weakTm.get());
-
+            assertNull("TetheringManager weak reference is not null", weakTm.get());
             assertEquals("Internal callback is not unregistered", 0, callbacks.size());
         });
     }
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index a36d67f..a8d886b 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -196,6 +196,7 @@
 import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
 import com.android.networkstack.tethering.metrics.TetheringMetrics;
 import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.MiscAsserts;
 
@@ -399,6 +400,7 @@
                     MacAddress.ALL_ZEROS_ADDRESS);
         }
 
+        @SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run()
         @Override
         public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb) {
@@ -1211,13 +1213,12 @@
         inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
     }
 
-    @Test
-    public void testAutomaticUpstreamSelection() throws Exception {
+    private void verifyAutomaticUpstreamSelection(boolean configAutomatic) throws Exception {
         TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
         TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
         InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
         // Enable automatic upstream selection.
-        upstreamSelectionTestCommon(true, inOrder, mobile, wifi);
+        upstreamSelectionTestCommon(configAutomatic, inOrder, mobile, wifi);
 
         // This code has historically been racy, so test different orderings of CONNECTIVITY_ACTION
         // broadcasts and callbacks, and add mLooper.dispatchAll() calls between the two.
@@ -1297,6 +1298,20 @@
     }
 
     @Test
+    public void testAutomaticUpstreamSelection() throws Exception {
+        verifyAutomaticUpstreamSelection(true /* configAutomatic */);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    public void testAutomaticUpstreamSelectionWithConfigDisabled() throws Exception {
+        // Expect that automatic config can't disable the automatic mode because automatic mode
+        // is always enabled on U+ device.
+        verifyAutomaticUpstreamSelection(false /* configAutomatic */);
+    }
+
+    @Test
+    @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testLegacyUpstreamSelection() throws Exception {
         TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
         TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
@@ -1322,14 +1337,13 @@
         verifyDisableTryCellWhenTetheringStop(inOrder);
     }
 
-    @Test
-    public void testChooseDunUpstreamByAutomaticMode() throws Exception {
+    private void verifyChooseDunUpstreamByAutomaticMode(boolean configAutomatic) throws Exception {
         // Enable automatic upstream selection.
         TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
         TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
         TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
         InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
-        chooseDunUpstreamTestCommon(true, inOrder, mobile, wifi, dun);
+        chooseDunUpstreamTestCommon(configAutomatic, inOrder, mobile, wifi, dun);
 
         // When default network switch to mobile and wifi is connected (may have low signal),
         // automatic mode would request dun again and choose it as upstream.
@@ -1358,6 +1372,18 @@
     }
 
     @Test
+    public void testChooseDunUpstreamByAutomaticMode() throws Exception {
+        verifyChooseDunUpstreamByAutomaticMode(true /* configAutomatic */);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    public void testChooseDunUpstreamByAutomaticModeWithConfigDisabled() throws Exception {
+        verifyChooseDunUpstreamByAutomaticMode(false /* configAutomatic */);
+    }
+
+    @Test
+    @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testChooseDunUpstreamByLegacyMode() throws Exception {
         // Enable Legacy upstream selection.
         TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
@@ -1854,20 +1880,12 @@
 
         private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual,
                 @NonNull TetheringConfigurationParcel expect) {
-            assertEquals(actual.subId, expect.subId);
             assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs);
             assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs);
             assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs);
-            assertEquals(actual.isDunRequired, expect.isDunRequired);
-            assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically);
-            assertArrayEquals(actual.preferredUpstreamIfaceTypes,
-                    expect.preferredUpstreamIfaceTypes);
             assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges);
-            assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS);
-            assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer);
             assertArrayEquals(actual.provisioningApp, expect.provisioningApp);
             assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi);
-            assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod);
         }
     }
 
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index b0cb7f2..9b9507b 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -449,36 +449,6 @@
     }
 
     @Test
-    public void testGetCurrentPreferredUpstream_TestNetworkPreferred() throws Exception {
-        mUNM.startTrackDefaultNetwork(mEntitleMgr);
-        mUNM.startObserveAllNetworks();
-        mUNM.setUpstreamConfig(true /* autoUpstream */, false /* dunRequired */);
-        mUNM.setTryCell(true);
-        mUNM.setPreferTestNetworks(true);
-
-        // [1] Mobile connects, DUN not required -> mobile selected.
-        final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
-        cellAgent.fakeConnect();
-        mCM.makeDefaultNetwork(cellAgent);
-        mLooper.dispatchAll();
-        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-        assertEquals(0, mCM.mRequested.size());
-
-        // [2] Test network connects -> test network selected.
-        final TestNetworkAgent testAgent =
-                TestNetworkAgent.buildTestNetworkAgentForTestNetwork(mCM);
-        testAgent.fakeConnect();
-        mLooper.dispatchAll();
-        assertEquals(testAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-        assertEquals(0, mCM.mRequested.size());
-
-        // [3] Disable test networks preferred -> mobile selected.
-        mUNM.setPreferTestNetworks(false);
-        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
-        assertEquals(0, mCM.mRequested.size());
-    }
-
-    @Test
     public void testLocalPrefixes() throws Exception {
         mUNM.startTrackDefaultNetwork(mEntitleMgr);
         mUNM.startObserveAllNetworks();
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index 6a85718..7fdde97 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -88,6 +88,8 @@
                 .setUserType(user)
                 .setUpstreamType(UpstreamType.UT_UNKNOWN)
                 .setErrorCode(error)
+                .setUpstreamEvents(UpstreamEvents.newBuilder())
+                .setDurationMillis(0)
                 .build();
         verify(mTetheringMetrics).write(expectedReport);
     }
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index e382713..c39269e 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -28,9 +28,12 @@
 
 static int (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data;
 
-static int (*bpf_skb_load_bytes)(struct __sk_buff* skb, int off, void* to,
+static int (*bpf_skb_load_bytes)(const struct __sk_buff* skb, int off, void* to,
                                  int len) = (void*)BPF_FUNC_skb_load_bytes;
 
+static int (*bpf_skb_load_bytes_relative)(const struct __sk_buff* skb, int off, void* to, int len,
+                                          int start_hdr) = (void*)BPF_FUNC_skb_load_bytes_relative;
+
 static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
                                   __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
 
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index 85b9f86..7b1106a 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -95,13 +95,13 @@
 
 // 'static' - otherwise these constants end up in .rodata in the resulting .o post compilation
 static const int COOKIE_UID_MAP_SIZE = 10000;
-static const int UID_COUNTERSET_MAP_SIZE = 2000;
+static const int UID_COUNTERSET_MAP_SIZE = 4000;
 static const int APP_STATS_MAP_SIZE = 10000;
 static const int STATS_MAP_SIZE = 5000;
 static const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
 static const int IFACE_STATS_MAP_SIZE = 1000;
 static const int CONFIGURATION_MAP_SIZE = 2;
-static const int UID_OWNER_MAP_SIZE = 2000;
+static const int UID_OWNER_MAP_SIZE = 4000;
 
 #ifdef __cplusplus
 
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index a2214dc..fc10d09 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -38,8 +38,19 @@
 #include "bpf_shared.h"
 #include "clat_mark.h"
 
-// From kernel:include/net/ip.h
-#define IP_DF 0x4000  // Flag: "Don't Fragment"
+// IP flags. (from kernel's include/net/ip.h)
+#define IP_CE      0x8000  // Flag: "Congestion" (really reserved 'evil bit')
+#define IP_DF      0x4000  // Flag: "Don't Fragment"
+#define IP_MF      0x2000  // Flag: "More Fragments"
+#define IP_OFFSET  0x1FFF  // "Fragment Offset" part
+
+// from kernel's include/net/ipv6.h
+struct frag_hdr {
+    __u8   nexthdr;
+    __u8   reserved;        // always zero
+    __be16 frag_off;        // 13 bit offset, 2 bits zero, 1 bit "More Fragments"
+    __be32 identification;
+};
 
 DEFINE_BPF_MAP_GRW(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16, AID_SYSTEM)
 
@@ -89,7 +100,33 @@
 
     if (!v) return TC_ACT_PIPE;
 
-    switch (ip6->nexthdr) {
+    __u8 proto = ip6->nexthdr;
+    __be16 ip_id = 0;
+    __be16 frag_off = htons(IP_DF);
+    __u16 tot_len = ntohs(ip6->payload_len) + sizeof(struct iphdr);  // cannot overflow, see above
+
+    if (proto == IPPROTO_FRAGMENT) {
+        // Must have (ethernet and) ipv6 header and ipv6 fragment extension header
+        if (data + l2_header_size + sizeof(*ip6) + sizeof(struct frag_hdr) > data_end)
+            return TC_ACT_PIPE;
+        const struct frag_hdr *frag = (const struct frag_hdr *)(ip6 + 1);
+        proto = frag->nexthdr;
+        // RFC6145: use bottom 16-bits of network endian 32-bit IPv6 ID field for 16-bit IPv4 field.
+        // this is equivalent to: ip_id = htons(ntohl(frag->identification));
+        ip_id = frag->identification >> 16;
+        // Conversion of 16-bit IPv6 frag offset to 16-bit IPv4 frag offset field.
+        // IPv6 is '13 bits of offset in multiples of 8' + 2 zero bits + more fragment bit
+        // IPv4 is zero bit + don't frag bit + more frag bit + '13 bits of offset in multiples of 8'
+        frag_off = ntohs(frag->frag_off);
+        frag_off = ((frag_off & 1) << 13) | (frag_off >> 3);
+        frag_off = htons(frag_off);
+        // Note that by construction tot_len is guaranteed to not underflow here
+        tot_len -= sizeof(struct frag_hdr);
+        // This is a badly formed IPv6 packet with less payload than the size of an IPv6 Frag EH
+        if (tot_len < sizeof(struct iphdr)) return TC_ACT_PIPE;
+    }
+
+    switch (proto) {
         case IPPROTO_TCP:  // For TCP & UDP the checksum neutrality of the chosen IPv6
         case IPPROTO_UDP:  // address means there is no need to update their checksums.
         case IPPROTO_GRE:  // We do not need to bother looking at GRE/ESP headers,
@@ -114,14 +151,14 @@
             .version = 4,                                                      // u4
             .ihl = sizeof(struct iphdr) / sizeof(__u32),                       // u4
             .tos = (ip6->priority << 4) + (ip6->flow_lbl[0] >> 4),             // u8
-            .tot_len = htons(ntohs(ip6->payload_len) + sizeof(struct iphdr)),  // u16
-            .id = 0,                                                           // u16
-            .frag_off = htons(IP_DF),                                          // u16
+            .tot_len = htons(tot_len),                                         // be16
+            .id = ip_id,                                                       // be16
+            .frag_off = frag_off,                                              // be16
             .ttl = ip6->hop_limit,                                             // u8
-            .protocol = ip6->nexthdr,                                          // u8
+            .protocol = proto,                                                 // u8
             .check = 0,                                                        // u16
-            .saddr = ip6->saddr.in6_u.u6_addr32[3],                            // u32
-            .daddr = v->local4.s_addr,                                         // u32
+            .saddr = ip6->saddr.in6_u.u6_addr32[3],                            // be32
+            .daddr = v->local4.s_addr,                                         // be32
     };
 
     // Calculate the IPv4 one's complement checksum of the IPv4 header.
@@ -171,6 +208,13 @@
     //   return -ENOTSUPP;
     bpf_csum_update(skb, sum6);
 
+    if (frag_off != htons(IP_DF)) {
+        // If we're converting an IPv6 Fragment, we need to trim off 8 more bytes
+        // We're beyond recovery on error here... but hard to imagine how this could fail.
+        if (bpf_skb_adjust_room(skb, -(__s32)sizeof(struct frag_hdr), BPF_ADJ_ROOM_NET, /*flags*/0))
+            return TC_ACT_SHOT;
+    }
+
     // bpf_skb_change_proto() invalidates all pointers - reload them.
     data = (void*)(long)skb->data;
     data_end = (void*)(long)skb->data_end;
@@ -211,11 +255,6 @@
 
 DEFINE_BPF_MAP_GRW(clat_egress4_map, HASH, ClatEgress4Key, ClatEgress4Value, 16, AID_SYSTEM)
 
-DEFINE_BPF_PROG("schedcls/egress4/clat_ether", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_ether)
-(struct __sk_buff* skb) {
-    return TC_ACT_PIPE;
-}
-
 DEFINE_BPF_PROG("schedcls/egress4/clat_rawip", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_rawip)
 (struct __sk_buff* skb) {
     // Must be meta-ethernet IPv4 frame
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 10559dd..8e99b8d 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -34,22 +34,30 @@
 #include "bpf_shared.h"
 
 // This is defined for cgroup bpf filter only.
-#define BPF_DROP_UNLESS_DNS 2
-#define BPF_PASS 1
-#define BPF_DROP 0
+static const int DROP = 0;
+static const int PASS = 1;
+static const int DROP_UNLESS_DNS = 2;  // internal to our program
 
 // This is used for xt_bpf program only.
-#define BPF_NOMATCH 0
-#define BPF_MATCH 1
+static const int BPF_NOMATCH = 0;
+static const int BPF_MATCH = 1;
 
-#define BPF_EGRESS 0
-#define BPF_INGRESS 1
+// Used for 'bool egress'
+static const bool INGRESS = false;
+static const bool EGRESS = true;
 
 #define IP_PROTO_OFF offsetof(struct iphdr, protocol)
 #define IPV6_PROTO_OFF offsetof(struct ipv6hdr, nexthdr)
+
+// offsetof(struct iphdr, ihl) -- but that's a bitfield
 #define IPPROTO_IHL_OFF 0
-#define TCP_FLAG_OFF 13
-#define RST_OFFSET 2
+
+// This is offsetof(struct tcphdr, "32 bit tcp flag field")
+// The tcp flags are after be16 source, dest & be32 seq, ack_seq, hence 12 bytes in.
+//
+// Note that TCP_FLAG_{ACK,PSH,RST,SYN,FIN} are htonl(0x00{10,08,04,02,01}0000)
+// see include/uapi/linux/tcp.h
+#define TCP_FLAG32_OFF 12
 
 // For maps netd does not need to access
 #define DEFINE_BPF_MAP_NO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
@@ -72,6 +80,9 @@
 // only valid indexes are [0..CONFIGURATION_MAP_SIZE-1]
 DEFINE_BPF_MAP_RO_NETD(configuration_map, ARRAY, uint32_t, uint32_t, CONFIGURATION_MAP_SIZE)
 
+// TODO: consider whether we can merge some of these maps
+// for example it might be possible to merge 2 or 3 of:
+//   uid_counterset_map + uid_owner_map + uid_permission_map
 DEFINE_BPF_MAP_RW_NETD(cookie_tag_map, HASH, uint64_t, UidTagValue, COOKIE_UID_MAP_SIZE)
 DEFINE_BPF_MAP_NO_NETD(uid_counterset_map, HASH, uint32_t, uint8_t, UID_COUNTERSET_MAP_SIZE)
 DEFINE_BPF_MAP_NO_NETD(app_uid_stats_map, HASH, uint32_t, StatsValue, APP_STATS_MAP_SIZE)
@@ -97,9 +108,15 @@
 // programs that need to be usable by netd, but not by netutils_wrappers
 // (this is because these are currently attached by the mainline provided libnetd_updatable .so
 // which is loaded into netd and thus runs as netd uid/gid/selinux context)
-#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+#define DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, minKV, maxKV) \
     DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, \
-                        KVER_NONE, KVER_INF, false, "fs_bpf_netd_readonly", "")
+                        minKV, maxKV, false, "fs_bpf_netd_readonly", "")
+
+#define DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
+    DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF)
+
+#define DEFINE_NETD_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
+    DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE)
 
 // programs that only need to be usable by the system server
 #define DEFINE_SYS_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
@@ -141,7 +158,7 @@
  */
 #define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey)                                          \
     static __always_inline inline void update_##the_stats_map(struct __sk_buff* skb,           \
-                                                              int direction, TypeOfKey* key) { \
+                                                              bool egress, TypeOfKey* key) {   \
         StatsValue* value = bpf_##the_stats_map##_lookup_elem(key);                            \
         if (!value) {                                                                          \
             StatsValue newValue = {};                                                          \
@@ -161,10 +178,10 @@
                 packets = (payload + mss - 1) / mss;                                           \
                 bytes = tcp_overhead * packets + payload;                                      \
             }                                                                                  \
-            if (direction == BPF_EGRESS) {                                                     \
+            if (egress) {                                                                      \
                 __sync_fetch_and_add(&value->txPackets, packets);                              \
                 __sync_fetch_and_add(&value->txBytes, bytes);                                  \
-            } else if (direction == BPF_INGRESS) {                                             \
+            } else {                                                                           \
                 __sync_fetch_and_add(&value->rxPackets, packets);                              \
                 __sync_fetch_and_add(&value->rxBytes, bytes);                                  \
             }                                                                                  \
@@ -176,46 +193,48 @@
 DEFINE_UPDATE_STATS(stats_map_A, StatsKey)
 DEFINE_UPDATE_STATS(stats_map_B, StatsKey)
 
-static inline bool skip_owner_match(struct __sk_buff* skb) {
-    int offset = -1;
-    int ret = 0;
-    if (skb->protocol == htons(ETH_P_IP)) {
-        offset = IP_PROTO_OFF;
-        uint8_t proto, ihl;
-        uint8_t flag;
-        ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
-        if (!ret) {
-            if (proto == IPPROTO_ESP) {
-                return true;
-            } else if (proto == IPPROTO_TCP) {
-                ret = bpf_skb_load_bytes(skb, IPPROTO_IHL_OFF, &ihl, 1);
-                ihl = ihl & 0x0F;
-                ret = bpf_skb_load_bytes(skb, ihl * 4 + TCP_FLAG_OFF, &flag, 1);
-                if (ret == 0 && (flag >> RST_OFFSET & 1)) {
-                    return true;
-                }
-            }
-        }
-    } else if (skb->protocol == htons(ETH_P_IPV6)) {
-        offset = IPV6_PROTO_OFF;
-        uint8_t proto;
-        ret = bpf_skb_load_bytes(skb, offset, &proto, 1);
-        if (!ret) {
-            if (proto == IPPROTO_ESP) {
-                return true;
-            } else if (proto == IPPROTO_TCP) {
-                uint8_t flag;
-                ret = bpf_skb_load_bytes(skb, sizeof(struct ipv6hdr) + TCP_FLAG_OFF, &flag, 1);
-                if (ret == 0 && (flag >> RST_OFFSET & 1)) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
+// both of these return 0 on success or -EFAULT on failure (and zero out the buffer)
+static __always_inline inline int bpf_skb_load_bytes_net(const struct __sk_buff* skb, int off,
+                                                         void* to, int len, bool is_4_19) {
+    return is_4_19
+        ? bpf_skb_load_bytes_relative(skb, off, to, len, BPF_HDR_START_NET)
+        : bpf_skb_load_bytes(skb, off, to, len);
 }
 
-static __always_inline BpfConfig getConfig(uint32_t configKey) {
+static __always_inline inline bool skip_owner_match(struct __sk_buff* skb, bool is_4_19) {
+    uint32_t flag = 0;
+    if (skb->protocol == htons(ETH_P_IP)) {
+        uint8_t proto;
+        // no need to check for success, proto will be zeroed if bpf_skb_load_bytes_net() fails
+        (void)bpf_skb_load_bytes_net(skb, IP_PROTO_OFF, &proto, sizeof(proto), is_4_19);
+        if (proto == IPPROTO_ESP) return true;
+        if (proto != IPPROTO_TCP) return false;  // handles read failure above
+        uint8_t ihl;
+        // we don't check for success, as this cannot fail, as it is earlier in the packet than
+        // proto, the reading of which must have succeeded, additionally the next read
+        // (a little bit deeper in the packet in spite of ihl being zeroed) of the tcp flags
+        // field will also fail, and that failure we already handle correctly
+        // (we also don't check that ihl in [0x45,0x4F] nor that ipv4 header checksum is correct)
+        (void)bpf_skb_load_bytes_net(skb, IPPROTO_IHL_OFF, &ihl, sizeof(ihl), is_4_19);
+        // if the read below fails, we'll just assume no TCP flags are set, which is fine.
+        (void)bpf_skb_load_bytes_net(skb, (ihl & 0xF) * 4 + TCP_FLAG32_OFF,
+                                     &flag, sizeof(flag), is_4_19);
+    } else if (skb->protocol == htons(ETH_P_IPV6)) {
+        uint8_t proto;
+        // no need to check for success, proto will be zeroed if bpf_skb_load_bytes_net() fails
+        (void)bpf_skb_load_bytes_net(skb, IPV6_PROTO_OFF, &proto, sizeof(proto), is_4_19);
+        if (proto == IPPROTO_ESP) return true;
+        if (proto != IPPROTO_TCP) return false;  // handles read failure above
+        // if the read below fails, we'll just assume no TCP flags are set, which is fine.
+        (void)bpf_skb_load_bytes_net(skb, sizeof(struct ipv6hdr) + TCP_FLAG32_OFF,
+                                     &flag, sizeof(flag), is_4_19);
+    } else {
+        return false;
+    }
+    return flag & TCP_FLAG_RST;  // false on read failure
+}
+
+static __always_inline inline BpfConfig getConfig(uint32_t configKey) {
     uint32_t mapSettingKey = configKey;
     BpfConfig* config = bpf_configuration_map_lookup_elem(&mapSettingKey);
     if (!config) {
@@ -225,15 +244,16 @@
     return *config;
 }
 
-// DROP_IF_SET is set of rules that BPF_DROP if rule is globally enabled, and per-uid bit is set
+// DROP_IF_SET is set of rules that DROP if rule is globally enabled, and per-uid bit is set
 #define DROP_IF_SET (STANDBY_MATCH | OEM_DENY_1_MATCH | OEM_DENY_2_MATCH | OEM_DENY_3_MATCH)
 // DROP_IF_UNSET is set of rules that should DROP if globally enabled, and per-uid bit is NOT set
 #define DROP_IF_UNSET (DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH | LOW_POWER_STANDBY_MATCH)
 
-static inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid, int direction) {
-    if (skip_owner_match(skb)) return BPF_PASS;
+static __always_inline inline int bpf_owner_match(struct __sk_buff* skb, uint32_t uid,
+                                                  bool egress, bool is_4_19) {
+    if (skip_owner_match(skb, is_4_19)) return PASS;
 
-    if (is_system_uid(uid)) return BPF_PASS;
+    if (is_system_uid(uid)) return PASS;
 
     BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
 
@@ -243,37 +263,38 @@
 
     // Warning: funky bit-wise arithmetic: in parallel, for all DROP_IF_SET/UNSET rules
     // check whether the rules are globally enabled, and if so whether the rules are
-    // set/unset for the specific uid.  BPF_DROP if that is the case for ANY of the rules.
+    // set/unset for the specific uid.  DROP if that is the case for ANY of the rules.
     // We achieve this by masking out only the bits/rules we're interested in checking,
     // and negating (via bit-wise xor) the bits/rules that should drop if unset.
-    if (enabledRules & (DROP_IF_SET | DROP_IF_UNSET) & (uidRules ^ DROP_IF_UNSET)) return BPF_DROP;
+    if (enabledRules & (DROP_IF_SET | DROP_IF_UNSET) & (uidRules ^ DROP_IF_UNSET)) return DROP;
 
-    if (direction == BPF_INGRESS && skb->ifindex != 1) {
+    if (!egress && skb->ifindex != 1) {
         if (uidRules & IIF_MATCH) {
             if (allowed_iif && skb->ifindex != allowed_iif) {
                 // Drops packets not coming from lo nor the allowed interface
                 // allowed interface=0 is a wildcard and does not drop packets
-                return BPF_DROP_UNLESS_DNS;
+                return DROP_UNLESS_DNS;
             }
         } else if (uidRules & LOCKDOWN_VPN_MATCH) {
             // Drops packets not coming from lo and rule does not have IIF_MATCH but has
             // LOCKDOWN_VPN_MATCH
-            return BPF_DROP_UNLESS_DNS;
+            return DROP_UNLESS_DNS;
         }
     }
-    return BPF_PASS;
+    return PASS;
 }
 
-static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, int direction,
+static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, bool egress,
                                                             StatsKey* key, uint32_t selectedMap) {
     if (selectedMap == SELECT_MAP_A) {
-        update_stats_map_A(skb, direction, key);
+        update_stats_map_A(skb, egress, key);
     } else if (selectedMap == SELECT_MAP_B) {
-        update_stats_map_B(skb, direction, key);
+        update_stats_map_B(skb, egress, key);
     }
 }
 
-static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, int direction) {
+static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, bool egress,
+                                                      bool is_4_19) {
     uint32_t sock_uid = bpf_get_socket_uid(skb);
     uint64_t cookie = bpf_get_socket_cookie(skb);
     UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
@@ -290,11 +311,11 @@
     // interface is accounted for and subject to usage restrictions.
     // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
     if (sock_uid == AID_CLAT || uid == AID_CLAT) {
-        return BPF_PASS;
+        return PASS;
     }
 
-    int match = bpf_owner_match(skb, sock_uid, direction);
-    if ((direction == BPF_EGRESS) && (match == BPF_DROP)) {
+    int match = bpf_owner_match(skb, sock_uid, egress, is_4_19);
+    if (egress && (match == DROP)) {
         // If an outbound packet is going to be dropped, we do not count that
         // traffic.
         return match;
@@ -306,9 +327,9 @@
 #define TAG_SYSTEM_DNS 0xFFFFFF82
     if (tag == TAG_SYSTEM_DNS && uid == AID_DNS) {
         uid = sock_uid;
-        if (match == BPF_DROP_UNLESS_DNS) match = BPF_PASS;
+        if (match == DROP_UNLESS_DNS) match = PASS;
     } else {
-        if (match == BPF_DROP_UNLESS_DNS) match = BPF_DROP;
+        if (match == DROP_UNLESS_DNS) match = DROP;
     }
 
     StatsKey key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
@@ -328,24 +349,38 @@
     }
 
     if (key.tag) {
-        update_stats_with_config(skb, direction, &key, *selectedMap);
+        update_stats_with_config(skb, egress, &key, *selectedMap);
         key.tag = 0;
     }
 
-    update_stats_with_config(skb, direction, &key, *selectedMap);
-    update_app_uid_stats_map(skb, direction, &uid);
+    update_stats_with_config(skb, egress, &key, *selectedMap);
+    update_app_uid_stats_map(skb, egress, &uid);
     asm("%0 &= 1" : "+r"(match));
     return match;
 }
 
-DEFINE_NETD_BPF_PROG("cgroupskb/ingress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_ingress)
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_19", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_ingress_4_19, KVER(4, 19, 0), KVER_INF)
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, BPF_INGRESS);
+    return bpf_traffic_account(skb, INGRESS, /* is_4_19 */ true);
 }
 
-DEFINE_NETD_BPF_PROG("cgroupskb/egress/stats", AID_ROOT, AID_SYSTEM, bpf_cgroup_egress)
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_14", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_ingress_4_14, KVER_NONE, KVER(4, 19, 0))
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, BPF_EGRESS);
+    return bpf_traffic_account(skb, INGRESS, /* is_4_19 */ false);
+}
+
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_19", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_egress_4_19, KVER(4, 19, 0), KVER_INF)
+(struct __sk_buff* skb) {
+    return bpf_traffic_account(skb, EGRESS, /* is_4_19 */ true);
+}
+
+DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_14", AID_ROOT, AID_SYSTEM,
+                                bpf_cgroup_egress_4_14, KVER_NONE, KVER(4, 19, 0))
+(struct __sk_buff* skb) {
+    return bpf_traffic_account(skb, EGRESS, /* is_4_19 */ false);
 }
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
@@ -364,7 +399,7 @@
     }
 
     uint32_t key = skb->ifindex;
-    update_iface_stats_map(skb, BPF_EGRESS, &key);
+    update_iface_stats_map(skb, EGRESS, &key);
     return BPF_MATCH;
 }
 
@@ -377,7 +412,7 @@
     // Keep that in mind when moving this out of iptables xt_bpf and into tc ingress (or xdp).
 
     uint32_t key = skb->ifindex;
-    update_iface_stats_map(skb, BPF_INGRESS, &key);
+    update_iface_stats_map(skb, INGRESS, &key);
     return BPF_MATCH;
 }
 
@@ -387,7 +422,7 @@
     if (is_received_skb(skb)) {
         // Account for ingress traffic before tc drops it.
         uint32_t key = skb->ifindex;
-        update_iface_stats_map(skb, BPF_INGRESS, &key);
+        update_iface_stats_map(skb, INGRESS, &key);
     }
     return TC_ACT_UNSPEC;
 }
@@ -419,7 +454,8 @@
     return BPF_NOMATCH;
 }
 
-DEFINE_NETD_BPF_PROG("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create)
+DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet/create", AID_ROOT, AID_ROOT, inet_socket_create,
+                          KVER(4, 14, 0))
 (struct bpf_sock* sk) {
     uint64_t gid_uid = bpf_get_current_uid_gid();
     /*
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index bb9fc34..e211d68 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -91,8 +91,11 @@
 
 // ----- Tethering Error Counters -----
 
-DEFINE_BPF_MAP_GRW(tether_error_map, ARRAY, uint32_t, uint32_t, BPF_TETHER_ERR__MAX,
-                   TETHERING_GID)
+// Note that pre-T devices with Mediatek chipsets may have a kernel bug (bad patch
+// "[ALPS05162612] bpf: fix ubsan error") making it impossible to write to non-zero
+// offset of bpf map ARRAYs.  This file (offload.o) loads on S+, but luckily this
+// array is only written by bpf code, and only read by userspace.
+DEFINE_BPF_MAP_RO(tether_error_map, ARRAY, uint32_t, uint32_t, BPF_TETHER_ERR__MAX, TETHERING_GID)
 
 #define COUNT_AND_RETURN(counter, ret) do {                     \
     uint32_t code = BPF_TETHER_ERR_ ## counter;                 \
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index d42205f..c11c358 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -40,6 +40,10 @@
 #define TETHERING_GID AID_NETWORK_STACK
 #endif
 
+// This is non production code, only used for testing
+// Needed because the bitmap array definition is non-kosher for pre-T OS devices.
+#define THIS_BPF_PROGRAM_IS_FOR_TEST_PURPOSES_ONLY
+
 #include "bpf_helpers.h"
 #include "bpf_net_helpers.h"
 #include "bpf_tethering.h"
diff --git a/common/src/com/android/net/module/util/bpf/ClatEgress4Key.java b/common/src/com/android/net/module/util/bpf/ClatEgress4Key.java
index 12200ec..f0af3dd 100644
--- a/common/src/com/android/net/module/util/bpf/ClatEgress4Key.java
+++ b/common/src/com/android/net/module/util/bpf/ClatEgress4Key.java
@@ -24,13 +24,13 @@
 
 /** Key type for clat egress IPv4 maps. */
 public class ClatEgress4Key extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long iif; // The input interface index
+    @Field(order = 0, type = Type.S32)
+    public final int iif; // The input interface index
 
     @Field(order = 1, type = Type.Ipv4Address)
     public final Inet4Address local4; // The source IPv4 address
 
-    public ClatEgress4Key(final long iif, final Inet4Address local4) {
+    public ClatEgress4Key(final int iif, final Inet4Address local4) {
         this.iif = iif;
         this.local4 = local4;
     }
diff --git a/common/src/com/android/net/module/util/bpf/ClatEgress4Value.java b/common/src/com/android/net/module/util/bpf/ClatEgress4Value.java
index c10cb4d..69fab09 100644
--- a/common/src/com/android/net/module/util/bpf/ClatEgress4Value.java
+++ b/common/src/com/android/net/module/util/bpf/ClatEgress4Value.java
@@ -24,8 +24,8 @@
 
 /** Value type for clat egress IPv4 maps. */
 public class ClatEgress4Value extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long oif; // The output interface to redirect to
+    @Field(order = 0, type = Type.S32)
+    public final int oif; // The output interface to redirect to
 
     @Field(order = 1, type = Type.Ipv6Address)
     public final Inet6Address local6; // The full 128-bits of the source IPv6 address
@@ -36,7 +36,7 @@
     @Field(order = 3, type = Type.U8, padding = 3)
     public final short oifIsEthernet; // Whether the output interface requires ethernet header
 
-    public ClatEgress4Value(final long oif, final Inet6Address local6, final Inet6Address pfx96,
+    public ClatEgress4Value(final int oif, final Inet6Address local6, final Inet6Address pfx96,
             final short oifIsEthernet) {
         this.oif = oif;
         this.local6 = local6;
diff --git a/common/src/com/android/net/module/util/bpf/ClatIngress6Key.java b/common/src/com/android/net/module/util/bpf/ClatIngress6Key.java
index 1e2f4e0..561113c 100644
--- a/common/src/com/android/net/module/util/bpf/ClatIngress6Key.java
+++ b/common/src/com/android/net/module/util/bpf/ClatIngress6Key.java
@@ -24,8 +24,8 @@
 
 /** Key type for clat ingress IPv6 maps. */
 public class ClatIngress6Key extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long iif; // The input interface index
+    @Field(order = 0, type = Type.S32)
+    public final int iif; // The input interface index
 
     @Field(order = 1, type = Type.Ipv6Address)
     public final Inet6Address pfx96; // The source /96 nat64 prefix, bottom 32 bits must be 0
@@ -33,7 +33,7 @@
     @Field(order = 2, type = Type.Ipv6Address)
     public final Inet6Address local6; // The full 128-bits of the destination IPv6 address
 
-    public ClatIngress6Key(final long iif, final Inet6Address pfx96, final Inet6Address local6) {
+    public ClatIngress6Key(final int iif, final Inet6Address pfx96, final Inet6Address local6) {
         this.iif = iif;
         this.pfx96 = pfx96;
         this.local6 = local6;
diff --git a/common/src/com/android/net/module/util/bpf/ClatIngress6Value.java b/common/src/com/android/net/module/util/bpf/ClatIngress6Value.java
index bfec44f..fb81caa 100644
--- a/common/src/com/android/net/module/util/bpf/ClatIngress6Value.java
+++ b/common/src/com/android/net/module/util/bpf/ClatIngress6Value.java
@@ -24,13 +24,13 @@
 
 /** Value type for clat ingress IPv6 maps. */
 public class ClatIngress6Value extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long oif; // The output interface to redirect to (0 means don't redirect)
+    @Field(order = 0, type = Type.S32)
+    public final int oif; // The output interface to redirect to (0 means don't redirect)
 
     @Field(order = 1, type = Type.Ipv4Address)
     public final Inet4Address local4; // The destination IPv4 address
 
-    public ClatIngress6Value(final long oif, final Inet4Address local4) {
+    public ClatIngress6Value(final int oif, final Inet4Address local4) {
         this.oif = oif;
         this.local4 = local4;
     }
diff --git a/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java b/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
index e1a221f..3fbd6fc 100644
--- a/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
+++ b/common/src/com/android/net/module/util/bpf/CookieTagMapValue.java
@@ -24,13 +24,13 @@
  * Value for cookie tag map.
  */
 public class CookieTagMapValue extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long uid;
+    @Field(order = 0, type = Type.S32)
+    public final int uid;
 
     @Field(order = 1, type = Type.U32)
     public final long tag;
 
-    public CookieTagMapValue(final long uid, final long tag) {
+    public CookieTagMapValue(final int uid, final long tag) {
         this.uid = uid;
         this.tag = tag;
     }
diff --git a/common/src/com/android/net/module/util/bpf/Tether4Key.java b/common/src/com/android/net/module/util/bpf/Tether4Key.java
index 8273e6a..738256a 100644
--- a/common/src/com/android/net/module/util/bpf/Tether4Key.java
+++ b/common/src/com/android/net/module/util/bpf/Tether4Key.java
@@ -75,7 +75,7 @@
                     Inet4Address.getByAddress(src4), Inet4Address.getByAddress(dst4),
                     Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort));
         } catch (UnknownHostException | IllegalArgumentException e) {
-            return String.format("Invalid IP address", e);
+            return "Invalid IP address" + e;
         }
     }
 }
diff --git a/common/src/com/android/net/module/util/bpf/Tether4Value.java b/common/src/com/android/net/module/util/bpf/Tether4Value.java
index 74fdda2..449c1da 100644
--- a/common/src/com/android/net/module/util/bpf/Tether4Value.java
+++ b/common/src/com/android/net/module/util/bpf/Tether4Value.java
@@ -91,7 +91,7 @@
                     Short.toUnsignedInt((short) srcPort), Short.toUnsignedInt((short) dstPort),
                     lastUsed);
         } catch (UnknownHostException | IllegalArgumentException e) {
-            return String.format("Invalid IP address", e);
+            return "Invalid IP address" + e;
         }
     }
 }
diff --git a/common/src/com/android/net/module/util/bpf/TetherStatsKey.java b/common/src/com/android/net/module/util/bpf/TetherStatsKey.java
index c6d595b..68111b6 100644
--- a/common/src/com/android/net/module/util/bpf/TetherStatsKey.java
+++ b/common/src/com/android/net/module/util/bpf/TetherStatsKey.java
@@ -22,32 +22,10 @@
 
 /** The key of BpfMap which is used for tethering stats. */
 public class TetherStatsKey extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long ifindex;  // upstream interface index
+    @Field(order = 0, type = Type.S32)
+    public final int ifindex;  // upstream interface index
 
-    public TetherStatsKey(final long ifindex) {
+    public TetherStatsKey(final int ifindex) {
         this.ifindex = ifindex;
     }
-
-    // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof TetherStatsKey)) return false;
-
-        final TetherStatsKey that = (TetherStatsKey) obj;
-
-        return ifindex == that.ifindex;
-    }
-
-    @Override
-    public int hashCode() {
-        return Long.hashCode(ifindex);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ifindex: %d", ifindex);
-    }
 }
diff --git a/common/src/com/android/net/module/util/bpf/TetherStatsValue.java b/common/src/com/android/net/module/util/bpf/TetherStatsValue.java
index 028d217..f05d1b7 100644
--- a/common/src/com/android/net/module/util/bpf/TetherStatsValue.java
+++ b/common/src/com/android/net/module/util/bpf/TetherStatsValue.java
@@ -47,34 +47,4 @@
         this.txBytes = txBytes;
         this.txErrors = txErrors;
     }
-
-    // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof TetherStatsValue)) return false;
-
-        final TetherStatsValue that = (TetherStatsValue) obj;
-
-        return rxPackets == that.rxPackets
-                && rxBytes == that.rxBytes
-                && rxErrors == that.rxErrors
-                && txPackets == that.txPackets
-                && txBytes == that.txBytes
-                && txErrors == that.txErrors;
-    }
-
-    @Override
-    public int hashCode() {
-        return Long.hashCode(rxPackets) ^ Long.hashCode(rxBytes) ^ Long.hashCode(rxErrors)
-                ^ Long.hashCode(txPackets) ^ Long.hashCode(txBytes) ^ Long.hashCode(txErrors);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("rxPackets: %s, rxBytes: %s, rxErrors: %s, txPackets: %s, "
-                + "txBytes: %s, txErrors: %s", rxPackets, rxBytes, rxErrors, txPackets,
-                txBytes, txErrors);
-    }
 }
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index 2e49307..d40fad9 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -75,6 +75,12 @@
     ],
 }
 
+filegroup {
+    name: "connectivity-t-hiddenapi-files",
+    srcs: ["hiddenapi/*.txt"],
+    visibility: ["//packages/modules/Connectivity:__subpackages__"],
+}
+
 java_library {
     name: "framework-connectivity-t-pre-jarjar",
     defaults: ["framework-connectivity-t-defaults"],
@@ -114,6 +120,19 @@
         "com.android.connectivity",
         "com.android.nearby",
     ],
+
+    hidden_api: {
+        max_target_o_low_priority: [
+            "hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt",
+        ],
+        max_target_r_low_priority: [
+            "hiddenapi/hiddenapi-max-target-r-loprio.txt",
+        ],
+        unsupported: [
+            "hiddenapi/hiddenapi-unsupported-tiramisu.txt",
+        ],
+    },
+
     impl_library_visibility: [
         "//packages/modules/Connectivity/Tethering/apex",
         // In preparation for future move
diff --git a/Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt b/framework-t/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt
similarity index 100%
rename from Tethering/apex/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt
rename to framework-t/hiddenapi/hiddenapi-max-target-o-low-priority-tiramisu.txt
diff --git a/Tethering/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt b/framework-t/hiddenapi/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from Tethering/apex/hiddenapi/hiddenapi-max-target-r-loprio.txt
rename to framework-t/hiddenapi/hiddenapi-max-target-r-loprio.txt
diff --git a/Tethering/apex/hiddenapi/hiddenapi-unsupported-tiramisu.txt b/framework-t/hiddenapi/hiddenapi-unsupported-tiramisu.txt
similarity index 100%
rename from Tethering/apex/hiddenapi/hiddenapi-unsupported-tiramisu.txt
rename to framework-t/hiddenapi/hiddenapi-unsupported-tiramisu.txt
diff --git a/framework-t/src/android/net/IpSecAlgorithm.java b/framework-t/src/android/net/IpSecAlgorithm.java
index 10a22ac..aac6f79 100644
--- a/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/framework-t/src/android/net/IpSecAlgorithm.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -351,8 +352,11 @@
             }
         }
 
+        // T introduced calculated property 'ro.vendor.api_level',
+        // which is the API level of the VSR that the device must conform to.
+        int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", 10000);
         for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) {
-            if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= entry.getValue()) {
+            if (vendorApiLevel >= entry.getValue()) {
                 enabledAlgos.add(entry.getKey());
             }
         }
@@ -488,4 +492,4 @@
                 && Arrays.equals(lhs.mKey, rhs.mKey)
                 && lhs.mTruncLenBits == rhs.mTruncLenBits);
     }
-};
+}
diff --git a/framework-t/src/android/net/NetworkIdentity.java b/framework-t/src/android/net/NetworkIdentity.java
index 350ed86..edfd21c 100644
--- a/framework-t/src/android/net/NetworkIdentity.java
+++ b/framework-t/src/android/net/NetworkIdentity.java
@@ -34,8 +34,8 @@
 import android.telephony.TelephonyManager;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.net.module.util.BitUtils;
 import com.android.net.module.util.CollectionUtils;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
 import java.lang.annotation.Retention;
@@ -172,7 +172,7 @@
         if (oemManaged == OEM_NONE) {
             return "OEM_NONE";
         }
-        final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+        final int[] bitPositions = BitUtils.unpackBits(oemManaged);
         final ArrayList<String> oemManagedNames = new ArrayList<String>();
         for (int position : bitPositions) {
             oemManagedNames.add(nameOfOemManaged(1 << position));
@@ -400,10 +400,8 @@
             setSubscriberId(snapshot.getSubscriberId());
             setRoaming(!snapshot.getNetworkCapabilities().hasCapability(
                     NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING));
-            setMetered(!(snapshot.getNetworkCapabilities().hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
-                    || snapshot.getNetworkCapabilities().hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)));
+            setMetered(!snapshot.getNetworkCapabilities().hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
 
             setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities()));
 
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index b6bd1a5..c0ae822 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -59,7 +58,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
@@ -197,149 +195,67 @@
      * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
      * the given IMSI.
      *
+     * @deprecated Use {@link Builder} to build a template.
      * @hide
      */
-    @UnsupportedAppUsage
-    public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
-        return new NetworkTemplate(MATCH_MOBILE, subscriberId, null);
-    }
-
-    /**
-     * Template to match cellular networks with the given IMSI, {@code ratType} and
-     * {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
-     * filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
-     *
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
-            int ratType, int metered) {
-        if (TextUtils.isEmpty(subscriberId)) {
-            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
-                    null /* matchSubscriberIds */,
-                    new String[0] /* matchWifiNetworkKeys */, metered, ROAMING_ALL,
-                    DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
-                    NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
-        }
-        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[] { subscriberId },
-                new String[0] /* matchWifiNetworkKeys */,
-                metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
-                NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code Builder} instead.")
+    public static NetworkTemplate buildTemplateMobileAll(@NonNull String subscriberId) {
+        return new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES)
+                .setSubscriberIds(Set.of(subscriberId)).build();
     }
 
     /**
      * Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks,
      * regardless of IMSI.
      *
+     * @deprecated Use {@link Builder} to build a template.
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static NetworkTemplate buildTemplateMobileWildcard() {
-        return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);
+        return new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build();
     }
 
     /**
      * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
      * regardless of key of the wifi network.
      *
+     * @deprecated Use {@link Builder} to build a template.
      * @hide
      */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code Builder} instead.")
     public static NetworkTemplate buildTemplateWifiWildcard() {
-        // TODO: Consider replace this with MATCH_WIFI with NETWORK_ID_ALL
-        // and SUBSCRIBER_ID_MATCH_RULE_ALL.
-        return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
+        return new NetworkTemplate.Builder(MATCH_WIFI).build();
     }
 
-    /** @hide */
+    /**
+     * @deprecated Use {@link Builder} to build a template.
+     * @hide
+     */
     @Deprecated
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code Builder} instead.")
     public static NetworkTemplate buildTemplateWifi() {
         return buildTemplateWifiWildcard();
     }
 
     /**
-     * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
-     * given key of the wifi network.
-     *
-     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
-     *                  to know details about the key.
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
-        Objects.requireNonNull(wifiNetworkKey);
-        return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
-                new String[] { null } /* matchSubscriberIds */,
-                new String[] { wifiNetworkKey }, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
-                NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL);
-    }
-
-    /**
-     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
-     * key of the wifi network and IMSI.
-     *
-     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
-     * of key of the wifi network.
-     *
-     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
-     *                  to know details about the key.
-     * @param subscriberId the IMSI associated to this wifi network.
-     *
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
-            @Nullable String subscriberId) {
-        return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
-                wifiNetworkKey != null
-                        ? new String[] { wifiNetworkKey } : new String[0],
-                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
-                NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
-    }
-
-    /**
      * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
      * networks together.
      *
+     * @deprecated Use {@link Builder} to build a template.
      * @hide
      */
-    @UnsupportedAppUsage
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code Builder} instead.")
     public static NetworkTemplate buildTemplateEthernet() {
-        return new NetworkTemplate(MATCH_ETHERNET, null, null);
-    }
-
-    /**
-     * Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style
-     * networks together.
-     *
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateBluetooth() {
-        return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
-    }
-
-    /**
-     * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
-     * networks together.
-     *
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateProxy() {
-        return new NetworkTemplate(MATCH_PROXY, null, null);
-    }
-
-    /**
-     * Template to match all metered carrier networks with the given IMSI.
-     *
-     * @hide
-     */
-    public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
-        Objects.requireNonNull(subscriberId);
-        return new NetworkTemplate(MATCH_CARRIER, subscriberId,
-                new String[] { subscriberId },
-                new String[0] /* matchWifiNetworkKeys */,
-                METERED_YES, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
-                NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
+        return new NetworkTemplate.Builder(MATCH_ETHERNET).build();
     }
 
     private final int mMatchRule;
@@ -353,6 +269,7 @@
      * Since the merge set is dynamic, it should <em>not</em> be persisted or
      * used for determining equality.
      */
+    @NonNull
     private final String[] mMatchSubscriberIds;
 
     @NonNull
@@ -387,9 +304,13 @@
         }
     }
 
-    /** @hide */
-    // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
-    @UnsupportedAppUsage
+    /**
+     * @deprecated Use {@link Builder} to build a template.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "Use {@code Builder} instead.")
     public NetworkTemplate(int matchRule, String subscriberId, String wifiNetworkKey) {
         this(matchRule, subscriberId, new String[] { subscriberId }, wifiNetworkKey);
     }
@@ -410,25 +331,13 @@
     }
 
     /** @hide */
-    // TODO: Remove it after updating all of the caller.
-    public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int ratType,
-            int oemManaged) {
-        this(matchRule, subscriberId, matchSubscriberIds,
-                wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
-                metered, roaming, defaultNetwork, ratType, oemManaged,
-                NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
-    }
-
-    /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String[] matchWifiNetworkKeys, int metered, int roaming,
             int defaultNetwork, int ratType, int oemManaged, int subscriberIdMatchRule) {
         Objects.requireNonNull(matchWifiNetworkKeys);
+        Objects.requireNonNull(matchSubscriberIds);
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
-        // TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
-        // mSubscriberId is null
         mMatchSubscriberIds = matchSubscriberIds;
         mMatchWifiNetworkKeys = matchWifiNetworkKeys;
         mMetered = metered;
@@ -881,48 +790,29 @@
      * For example, given an incoming template matching B, and the currently
      * active merge set [A,B], we'd return a new template that primarily matches
      * A, but also matches B.
-     * TODO: remove and use {@link #normalize(NetworkTemplate, List)}.
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+            publicAlternatives = "There is no alternative for {@code NetworkTemplate.normalize}."
+                    + "Callers should have their own logic to merge template for"
+                    + " different IMSIs and stop calling this function.")
     public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
-        return normalize(template, Arrays.<String[]>asList(merged));
-    }
-
-    /**
-     * Examine the given template and normalize it.
-     * We pick the "lowest" merged subscriber as the primary
-     * for key purposes, and expand the template to match all other merged
-     * subscribers.
-     *
-     * There can be multiple merged subscriberIds for multi-SIM devices.
-     *
-     * <p>
-     * For example, given an incoming template matching B, and the currently
-     * active merge set [A,B], we'd return a new template that primarily matches
-     * A, but also matches B.
-     *
-     * @hide
-     */
-    // TODO: @SystemApi when ready.
-    public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
         // Now there are several types of network which uses SubscriberId to store network
         // information. For instances:
         // The TYPE_WIFI with subscriberId means that it is a merged carrier wifi network.
         // The TYPE_CARRIER means that the network associate to specific carrier network.
-
         if (template.mSubscriberId == null) return template;
 
-        for (String[] merged : mergedList) {
-            if (CollectionUtils.contains(merged, template.mSubscriberId)) {
-                // Requested template subscriber is part of the merge group; return
-                // a template that matches all merged subscribers.
-                final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
-                return new NetworkTemplate(template.mMatchRule, merged[0], merged,
-                        CollectionUtils.isEmpty(matchWifiNetworkKeys)
-                                ? null : matchWifiNetworkKeys[0]);
-            }
+        if (CollectionUtils.contains(merged, template.mSubscriberId)) {
+            // Requested template subscriber is part of the merge group; return
+            // a template that matches all merged subscribers.
+            final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
+            // TODO: Use NetworkTemplate.Builder to build a template after NetworkTemplate
+            // could handle incompatible subscriberIds. See b/217805241.
+            return new NetworkTemplate(template.mMatchRule, merged[0], merged,
+                    CollectionUtils.isEmpty(matchWifiNetworkKeys)
+                            ? null : matchWifiNetworkKeys[0]);
         }
 
         return template;
diff --git a/framework/Android.bp b/framework/Android.bp
index fcce7a5..485961c 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -228,12 +228,13 @@
     ],
     out: ["framework_connectivity_jarjar_rules.txt"],
     cmd: "$(location jarjar-rules-generator) " +
-        "--jars $(location :framework-connectivity-pre-jarjar{.jar}) " +
+        "$(location :framework-connectivity-pre-jarjar{.jar}) " +
         "$(location :framework-connectivity-t-pre-jarjar{.jar}) " +
         "--prefix android.net.connectivity " +
         "--apistubs $(location :framework-connectivity.stubs.module_lib{.jar}) " +
-        "$(location :framework-connectivity-t.stubs.module_lib{.jar}) " +
-        "--unsupportedapi $(locations :connectivity-hiddenapi-files) " +
+        "--apistubs $(location :framework-connectivity-t.stubs.module_lib{.jar}) " +
+        // Make a ":"-separated list. There will be an extra ":" but empty items are ignored.
+        "--unsupportedapi $$(printf ':%s' $(locations :connectivity-hiddenapi-files)) " +
         "--excludes $(location jarjar-excludes.txt) " +
         "--output $(out)",
     visibility: [
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index a2a1ac0..752c347 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -15,7 +15,7 @@
     method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.LinkProperties getRedactedLinkPropertiesForPackage(@NonNull android.net.LinkProperties, int, @NonNull String);
     method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(@NonNull android.net.NetworkCapabilities, int, @NonNull String);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkAllowList(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkDenyList(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void replaceFirewallChain(int, @NonNull int[]);
@@ -228,13 +228,9 @@
   }
 
   public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
-    ctor public VpnTransportInfo(int, @Nullable String);
-    method public int describeContents();
+    ctor @Deprecated public VpnTransportInfo(int, @Nullable String);
     method @Nullable public String getSessionId();
-    method public int getType();
     method @NonNull public android.net.VpnTransportInfo makeCopy(long);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
   }
 
 }
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index f1298ce..c7872a0 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -433,6 +433,7 @@
   public abstract class QosFilter {
     method @NonNull public abstract android.net.Network getNetwork();
     method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
+    method public boolean matchesProtocol(int);
     method public abstract boolean matchesRemoteAddress(@NonNull java.net.InetAddress, int, int);
   }
 
@@ -453,6 +454,7 @@
 
   public final class QosSocketInfo implements android.os.Parcelable {
     ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
+    ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.DatagramSocket) throws java.io.IOException;
     method public int describeContents();
     method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
     method @NonNull public android.net.Network getNetwork();
@@ -480,6 +482,14 @@
     ctor public SocketNotBoundException();
   }
 
+  public class SocketNotConnectedException extends java.lang.Exception {
+    ctor public SocketNotConnectedException();
+  }
+
+  public class SocketRemoteAddressChangedException extends java.lang.Exception {
+    ctor public SocketRemoteAddressChangedException();
+  }
+
   public final class StaticIpConfiguration implements android.os.Parcelable {
     ctor public StaticIpConfiguration();
     ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -501,6 +511,15 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
   }
 
+  public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+    ctor public VpnTransportInfo(int, @Nullable String, boolean);
+    method public int describeContents();
+    method public boolean getBypassable();
+    method public int getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+  }
+
 }
 
 package android.net.apf {
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 547b4ba..39c5af2 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4080,7 +4080,7 @@
         }
     }
 
-    private class CallbackHandler extends Handler {
+    private static class CallbackHandler extends Handler {
         private static final String TAG = "ConnectivityManager.CallbackHandler";
         private static final boolean DBG = false;
 
@@ -4095,7 +4095,10 @@
         @Override
         public void handleMessage(Message message) {
             if (message.what == EXPIRE_LEGACY_REQUEST) {
-                expireRequest((NetworkCapabilities) message.obj, message.arg1);
+                // the sInstance can't be null because to send this message a ConnectivityManager
+                // instance must have been created prior to creating the thread on which this
+                // Handler is running.
+                sInstance.expireRequest((NetworkCapabilities) message.obj, message.arg1);
                 return;
             }
 
@@ -4786,7 +4789,8 @@
     @SuppressLint({"ExecutorRegistration", "PairedRegistration"})
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_SETTINGS})
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS})
     public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
             @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
index 5e637f9..c6034f1 100644
--- a/framework/src/android/net/DnsResolver.java
+++ b/framework/src/android/net/DnsResolver.java
@@ -542,7 +542,7 @@
 
         DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
             super(data);
-            if ((mHeader.flags & (1 << 15)) == 0) {
+            if ((mHeader.getFlags() & (1 << 15)) == 0) {
                 throw new ParseException("Not an answer packet");
             }
             if (mHeader.getRecordCount(QDSECTION) == 0) {
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index ea8a3df..e07601f 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
 
 import android.annotation.IntDef;
 import android.annotation.LongDef;
@@ -36,6 +37,7 @@
 import android.util.Range;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.BitUtils;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 
@@ -185,10 +187,18 @@
             NET_ENTERPRISE_ID_4,
             NET_ENTERPRISE_ID_5,
     })
-
     public @interface EnterpriseId {
     }
 
+    private static final int ALL_VALID_ENTERPRISE_IDS;
+    static {
+        int enterpriseIds = 0;
+        for (int i = NET_ENTERPRISE_ID_1; i <= NET_ENTERPRISE_ID_5; ++i) {
+            enterpriseIds |= 1 << i;
+        }
+        ALL_VALID_ENTERPRISE_IDS = enterpriseIds;
+    }
+
     /**
      * Bitfield representing the network's enterprise capability identifier.  If any are specified
      * they will be satisfied by any Network that matches all of them.
@@ -209,7 +219,7 @@
         if (hasCapability(NET_CAPABILITY_ENTERPRISE) && mEnterpriseId == 0) {
             return new int[]{NET_ENTERPRISE_ID_1};
         }
-        return NetworkCapabilitiesUtils.unpackBits(mEnterpriseId);
+        return BitUtils.unpackBits(mEnterpriseId);
     }
 
     /**
@@ -622,11 +632,20 @@
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
     private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
 
+    private static final int ALL_VALID_CAPABILITIES;
+    static {
+        int caps = 0;
+        for (int i = MIN_NET_CAPABILITY; i <= MAX_NET_CAPABILITY; ++i) {
+            caps |= 1 << i;
+        }
+        ALL_VALID_CAPABILITIES = caps;
+    }
+
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
      * network is connected.
      */
-    private static final long MUTABLE_CAPABILITIES = NetworkCapabilitiesUtils.packBitList(
+    private static final long MUTABLE_CAPABILITIES = BitUtils.packBitList(
             // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
             // http://b/18206275
             NET_CAPABILITY_TRUSTED,
@@ -661,7 +680,7 @@
     /**
      * Capabilities that are set by default when the object is constructed.
      */
-    private static final long DEFAULT_CAPABILITIES = NetworkCapabilitiesUtils.packBitList(
+    private static final long DEFAULT_CAPABILITIES = BitUtils.packBitList(
             NET_CAPABILITY_NOT_RESTRICTED,
             NET_CAPABILITY_TRUSTED,
             NET_CAPABILITY_NOT_VPN);
@@ -670,7 +689,7 @@
      * Capabilities that are managed by ConnectivityService.
      */
     private static final long CONNECTIVITY_MANAGED_CAPABILITIES =
-            NetworkCapabilitiesUtils.packBitList(
+            BitUtils.packBitList(
                     NET_CAPABILITY_VALIDATED,
                     NET_CAPABILITY_CAPTIVE_PORTAL,
                     NET_CAPABILITY_FOREGROUND,
@@ -683,7 +702,7 @@
      * INTERNET, IMS, SUPL, etc.
      */
     private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
-            NetworkCapabilitiesUtils.packBitList(
+            BitUtils.packBitList(
             NET_CAPABILITY_NOT_METERED,
             NET_CAPABILITY_TEMPORARILY_NOT_METERED,
             NET_CAPABILITY_NOT_RESTRICTED,
@@ -783,7 +802,7 @@
      * @return an array of capability values for this instance.
      */
     public @NonNull @NetCapability int[] getCapabilities() {
-        return NetworkCapabilitiesUtils.unpackBits(mNetworkCapabilities);
+        return BitUtils.unpackBits(mNetworkCapabilities);
     }
 
     /**
@@ -793,7 +812,7 @@
      * @hide
      */
     public @NetCapability int[] getForbiddenCapabilities() {
-        return NetworkCapabilitiesUtils.unpackBits(mForbiddenNetworkCapabilities);
+        return BitUtils.unpackBits(mForbiddenNetworkCapabilities);
     }
 
 
@@ -805,8 +824,8 @@
      */
     public void setCapabilities(@NetCapability int[] capabilities,
             @NetCapability int[] forbiddenCapabilities) {
-        mNetworkCapabilities = NetworkCapabilitiesUtils.packBits(capabilities);
-        mForbiddenNetworkCapabilities = NetworkCapabilitiesUtils.packBits(forbiddenCapabilities);
+        mNetworkCapabilities = BitUtils.packBits(capabilities);
+        mForbiddenNetworkCapabilities = BitUtils.packBits(forbiddenCapabilities);
     }
 
     /**
@@ -943,7 +962,7 @@
                 & NON_REQUESTABLE_CAPABILITIES;
 
         if (nonRequestable != 0) {
-            return capabilityNameOf(NetworkCapabilitiesUtils.unpackBits(nonRequestable)[0]);
+            return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
         }
         if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
         if (hasSignalStrength()) return "signalStrength";
@@ -1146,6 +1165,15 @@
     /** @hide */
     public static final int MAX_TRANSPORT = TRANSPORT_USB;
 
+    private static final int ALL_VALID_TRANSPORTS;
+    static {
+        int transports = 0;
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; ++i) {
+            transports |= 1 << i;
+        }
+        ALL_VALID_TRANSPORTS = transports;
+    }
+
     /** @hide */
     public static boolean isValidTransport(@Transport int transportType) {
         return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
@@ -1167,7 +1195,7 @@
      * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
      */
     private static final long UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
-            NetworkCapabilitiesUtils.packBitList(
+            BitUtils.packBitList(
                     TRANSPORT_TEST,
                     // Test eth networks are created with EthernetManager#setIncludeTestInterfaces
                     TRANSPORT_ETHERNET,
@@ -1232,7 +1260,7 @@
      */
     @SystemApi
     @NonNull public @Transport int[] getTransportTypes() {
-        return NetworkCapabilitiesUtils.unpackBits(mTransportTypes);
+        return BitUtils.unpackBits(mTransportTypes);
     }
 
     /**
@@ -1242,7 +1270,7 @@
      * @hide
      */
     public void setTransportTypes(@Transport int[] transportTypes) {
-        mTransportTypes = NetworkCapabilitiesUtils.packBits(transportTypes);
+        mTransportTypes = BitUtils.packBits(transportTypes);
     }
 
     /**
@@ -2015,9 +2043,9 @@
         long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
         long newImmutableCapabilities = that.mNetworkCapabilities & mask;
         if (oldImmutableCapabilities != newImmutableCapabilities) {
-            String before = capabilityNamesOf(NetworkCapabilitiesUtils.unpackBits(
+            String before = capabilityNamesOf(BitUtils.unpackBits(
                     oldImmutableCapabilities));
-            String after = capabilityNamesOf(NetworkCapabilitiesUtils.unpackBits(
+            String after = capabilityNamesOf(BitUtils.unpackBits(
                     newImmutableCapabilities));
             joiner.add(String.format("immutable capabilities changed: %s -> %s", before, after));
         }
@@ -2038,6 +2066,36 @@
     }
 
     /**
+     * Returns a short but human-readable string of updates from an older set of capabilities.
+     * @param old the old capabilities to diff from
+     * @return a string fit for logging differences, or null if no differences.
+     *         this never returns the empty string.
+     * @hide
+     */
+    @Nullable
+    public String describeCapsDifferencesFrom(@Nullable final NetworkCapabilities old) {
+        final long oldCaps = null == old ? 0 : old.mNetworkCapabilities;
+        final long changed = oldCaps ^ mNetworkCapabilities;
+        if (0 == changed) return null;
+        // If the control reaches here, there are changes (additions, removals, or both) so
+        // the code below is guaranteed to add something to the string and can't return "".
+        final long removed = oldCaps & changed;
+        final long added = mNetworkCapabilities & changed;
+        final StringBuilder sb = new StringBuilder();
+        if (0 != removed) {
+            sb.append("-");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, removed,
+                    NetworkCapabilities::capabilityNameOf, "-");
+        }
+        if (0 != added) {
+            sb.append("+");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, added,
+                    NetworkCapabilities::capabilityNameOf, "+");
+        }
+        return sb.toString();
+    }
+
+    /**
      * Checks that our requestable capabilities are the same as those of the given
      * {@code NetworkCapabilities}.
      *
@@ -2114,9 +2172,9 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(mNetworkCapabilities);
-        dest.writeLong(mForbiddenNetworkCapabilities);
-        dest.writeLong(mTransportTypes);
+        dest.writeLong(mNetworkCapabilities & ALL_VALID_CAPABILITIES);
+        dest.writeLong(mForbiddenNetworkCapabilities & ALL_VALID_CAPABILITIES);
+        dest.writeLong(mTransportTypes & ALL_VALID_TRANSPORTS);
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
@@ -2132,7 +2190,7 @@
         dest.writeString(mRequestorPackageName);
         dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
         dest.writeTypedList(mUnderlyingNetworks);
-        dest.writeInt(mEnterpriseId);
+        dest.writeInt(mEnterpriseId & ALL_VALID_ENTERPRISE_IDS);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -2140,10 +2198,10 @@
             @Override
             public NetworkCapabilities createFromParcel(Parcel in) {
                 NetworkCapabilities netCap = new NetworkCapabilities();
-
-                netCap.mNetworkCapabilities = in.readLong();
-                netCap.mForbiddenNetworkCapabilities = in.readLong();
-                netCap.mTransportTypes = in.readLong();
+                // Validate the unparceled data, in case the parceling party was malicious.
+                netCap.mNetworkCapabilities = in.readLong() & ALL_VALID_CAPABILITIES;
+                netCap.mForbiddenNetworkCapabilities = in.readLong() & ALL_VALID_CAPABILITIES;
+                netCap.mTransportTypes = in.readLong() & ALL_VALID_TRANSPORTS;
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
                 netCap.mNetworkSpecifier = in.readParcelable(null);
@@ -2167,7 +2225,7 @@
                     netCap.mSubIds.add(subIdInts[i]);
                 }
                 netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
-                netCap.mEnterpriseId = in.readInt();
+                netCap.mEnterpriseId = in.readInt() & ALL_VALID_ENTERPRISE_IDS;
                 return netCap;
             }
             @Override
@@ -2287,32 +2345,6 @@
         return sb.toString();
     }
 
-
-    private interface NameOf {
-        String nameOf(int value);
-    }
-
-    /**
-     * @hide
-     */
-    public static void appendStringRepresentationOfBitMaskToStringBuilder(@NonNull StringBuilder sb,
-            long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator) {
-        int bitPos = 0;
-        boolean firstElementAdded = false;
-        while (bitMask != 0) {
-            if ((bitMask & 1) != 0) {
-                if (firstElementAdded) {
-                    sb.append(separator);
-                } else {
-                    firstElementAdded = true;
-                }
-                sb.append(nameFetcher.nameOf(bitPos));
-            }
-            bitMask >>= 1;
-            ++bitPos;
-        }
-    }
-
     /**
      * @hide
      */
diff --git a/framework/src/android/net/QosFilter.java b/framework/src/android/net/QosFilter.java
index 01dc4bb..a731b23 100644
--- a/framework/src/android/net/QosFilter.java
+++ b/framework/src/android/net/QosFilter.java
@@ -97,10 +97,15 @@
      * Determines whether or not the parameter will be matched with this filter.
      *
      * @param protocol the protocol such as TCP or UDP included in IP packet filter set of a QoS
-     *                 flow assigned on {@link Network}.
+     *                 flow assigned on {@link Network}. Only {@code IPPROTO_TCP} and {@code
+     *                 IPPROTO_UDP} currently supported.
      * @return whether the parameters match the socket type of the filter
-     * @hide
      */
-    public abstract boolean matchesProtocol(int protocol);
+    // Since this method is added in U, it's required to be default method for binary compatibility
+    // with existing @SystemApi.
+    // IPPROTO_* are not compile-time constants, so they are not annotated with @IntDef.
+    public boolean matchesProtocol(int protocol) {
+        return false;
+    }
 }
 
diff --git a/framework/src/android/net/QosSocketInfo.java b/framework/src/android/net/QosSocketInfo.java
index da9b356..1c3db23 100644
--- a/framework/src/android/net/QosSocketInfo.java
+++ b/framework/src/android/net/QosSocketInfo.java
@@ -144,7 +144,6 @@
      *
      * @param network the network
      * @param socket the bound {@link DatagramSocket}
-     * @hide
      */
     public QosSocketInfo(@NonNull final Network network, @NonNull final DatagramSocket socket)
             throws IOException {
diff --git a/framework/src/android/net/SocketNotConnectedException.java b/framework/src/android/net/SocketNotConnectedException.java
index fa2a615..a6357c7 100644
--- a/framework/src/android/net/SocketNotConnectedException.java
+++ b/framework/src/android/net/SocketNotConnectedException.java
@@ -16,13 +16,18 @@
 
 package android.net;
 
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Thrown when a previously bound socket becomes unbound.
  *
  * @hide
  */
+@SystemApi
 public class SocketNotConnectedException extends Exception {
-    /** @hide */
+    @VisibleForTesting
     public SocketNotConnectedException() {
         super("The socket is not connected");
     }
diff --git a/framework/src/android/net/SocketRemoteAddressChangedException.java b/framework/src/android/net/SocketRemoteAddressChangedException.java
index ecaeebc..e13d5ed 100644
--- a/framework/src/android/net/SocketRemoteAddressChangedException.java
+++ b/framework/src/android/net/SocketRemoteAddressChangedException.java
@@ -16,13 +16,18 @@
 
 package android.net;
 
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Thrown when the local address of the socket has changed.
  *
  * @hide
  */
+@SystemApi
 public class SocketRemoteAddressChangedException extends Exception {
-    /** @hide */
+    @VisibleForTesting
     public SocketRemoteAddressChangedException() {
         super("The remote address of the socket changed");
     }
diff --git a/framework/src/android/net/TestNetworkInterface.java b/framework/src/android/net/TestNetworkInterface.java
index 26200e1..4c4e2d9 100644
--- a/framework/src/android/net/TestNetworkInterface.java
+++ b/framework/src/android/net/TestNetworkInterface.java
@@ -29,6 +29,9 @@
 /**
  * This class is used to return the interface name, fd, MAC, and MTU of the test interface
  *
+ * TestNetworkInterfaces are created by TestNetworkService and provide a
+ * wrapper around a tun/tap interface that can be used in integration tests.
+ *
  * @hide
  */
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -97,11 +100,25 @@
         return mInterfaceName;
     }
 
+    /**
+     * Returns the tap interface MacAddress.
+     *
+     * When TestNetworkInterface wraps a tun interface, the MAC address is null.
+     *
+     * @return the tap interface MAC address or null.
+     */
     @Nullable
     public MacAddress getMacAddress() {
         return mMacAddress;
     }
 
+    /**
+     * Returns the interface MTU.
+     *
+     * MTU defaults to 1500 if an error occurs.
+     *
+     * @return MTU in bytes.
+     */
     public int getMtu() {
         return mMtu;
     }
diff --git a/framework/src/android/net/VpnTransportInfo.java b/framework/src/android/net/VpnTransportInfo.java
index 4071c9a..ebad477 100644
--- a/framework/src/android/net/VpnTransportInfo.java
+++ b/framework/src/android/net/VpnTransportInfo.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
 import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 
 import android.annotation.NonNull;
@@ -27,6 +28,10 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import androidx.annotation.RequiresApi;
+
+import com.android.modules.utils.build.SdkLevel;
+
 import java.util.Objects;
 
 /**
@@ -37,7 +42,7 @@
  *
  * @hide
  */
-@SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = PRIVILEGED_APPS)
 public final class VpnTransportInfo implements TransportInfo, Parcelable {
     /** Type of this VPN. */
     private final int mType;
@@ -45,6 +50,13 @@
     @Nullable
     private final String mSessionId;
 
+    private final boolean mBypassable;
+
+    // TODO: Refer to Build.VERSION_CODES when it's available in every branch.
+    private static final int UPSIDE_DOWN_CAKE = 34;
+
+    /** @hide */
+    @SystemApi(client = MODULE_LIBRARIES)
     @Override
     public @RedactionType long getApplicableRedactions() {
         return REDACT_FOR_NETWORK_SETTINGS;
@@ -52,21 +64,58 @@
 
     /**
      * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary.
+     * @hide
      */
     @NonNull
+    @SystemApi(client = MODULE_LIBRARIES)
     public VpnTransportInfo makeCopy(@RedactionType long redactions) {
         return new VpnTransportInfo(mType,
-            ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : mSessionId);
+            ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : mSessionId, mBypassable);
     }
 
+    /**
+     * @deprecated please use {@link VpnTransportInfo(int,String,boolean)} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public VpnTransportInfo(int type, @Nullable String sessionId) {
+        // When the module runs on older SDKs, |bypassable| will always be false since the old Vpn
+        // code will call this constructor. For Settings VPNs, this is always correct as they are
+        // never bypassable. For VpnManager and VpnService types, this may be wrong since both of
+        // them have a choice. However, on these SDKs VpnTransportInfo#getBypassable is not
+        // available anyway, so this should be harmless. False is a better choice than true here
+        // regardless because it is the default value for both VpnManager and VpnService if the app
+        // does not do anything about it.
+        this(type, sessionId, false /* bypassable */);
+    }
+
+    public VpnTransportInfo(int type, @Nullable String sessionId, boolean bypassable) {
         this.mType = type;
         this.mSessionId = sessionId;
+        this.mBypassable = bypassable;
+    }
+
+    /**
+     * Returns whether the VPN is allowing bypass.
+     *
+     * This method is not supported in SDK below U, and will throw
+     * {@code UnsupportedOperationException} if called.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public boolean getBypassable() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException("Not supported before U");
+        }
+
+        return mBypassable;
     }
 
     /**
      * Returns the session Id of this VpnTransportInfo.
+     * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     @Nullable
     public String getSessionId() {
         return mSessionId;
@@ -84,17 +133,19 @@
         if (!(o instanceof VpnTransportInfo)) return false;
 
         VpnTransportInfo that = (VpnTransportInfo) o;
-        return (this.mType == that.mType) && TextUtils.equals(this.mSessionId, that.mSessionId);
+        return (this.mType == that.mType) && TextUtils.equals(this.mSessionId, that.mSessionId)
+                && (this.mBypassable == that.mBypassable);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mSessionId);
+        return Objects.hash(mType, mSessionId, mBypassable);
     }
 
     @Override
     public String toString() {
-        return String.format("VpnTransportInfo{type=%d, sessionId=%s}", mType, mSessionId);
+        return String.format("VpnTransportInfo{type=%d, sessionId=%s, bypassable=%b}",
+                mType, mSessionId, mBypassable);
     }
 
     @Override
@@ -106,12 +157,13 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mType);
         dest.writeString(mSessionId);
+        dest.writeBoolean(mBypassable);
     }
 
     public static final @NonNull Creator<VpnTransportInfo> CREATOR =
             new Creator<VpnTransportInfo>() {
         public VpnTransportInfo createFromParcel(Parcel in) {
-            return new VpnTransportInfo(in.readInt(), in.readString());
+            return new VpnTransportInfo(in.readInt(), in.readString(), in.readBoolean());
         }
         public VpnTransportInfo[] newArray(int size) {
             return new VpnTransportInfo[size];
diff --git a/framework/src/android/net/apf/ApfCapabilities.java b/framework/src/android/net/apf/ApfCapabilities.java
index 663c1b3..64f14a1 100644
--- a/framework/src/android/net/apf/ApfCapabilities.java
+++ b/framework/src/android/net/apf/ApfCapabilities.java
@@ -133,9 +133,7 @@
     public static boolean getApfDrop8023Frames() {
         // TODO: deprecate/remove this method (now unused in the platform), as the resource was
         // moved to NetworkStack.
-        final Resources systemRes = Resources.getSystem();
-        final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android");
-        return systemRes.getBoolean(id);
+        return true;
     }
 
     /**
@@ -144,8 +142,6 @@
     public static @NonNull int[] getApfEtherTypeBlackList() {
         // TODO: deprecate/remove this method (now unused in the platform), as the resource was
         // moved to NetworkStack.
-        final Resources systemRes = Resources.getSystem();
-        final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android");
-        return systemRes.getIntArray(id);
+        return new int[] { 0x88a2, 0x88a4, 0x88b8, 0x88cd, 0x88e3 };
     }
 }
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
deleted file mode 100644
index c1790c9..0000000
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.net.util;
-
-import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
-import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
-
-import android.annotation.NonNull;
-import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.ConnectivityResources;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.provider.Settings;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * A class to encapsulate management of the "Smart Networking" capability of
- * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
- * certain critical link failures occur.
- *
- * This enables the device to switch to another form of connectivity, like
- * mobile, if it's available and working.
- *
- * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
- * Handler' whenever the computed "avoid bad wifi" value changes.
- *
- * Disabling this reverts the device to a level of networking sophistication
- * circa 2012-13 by disabling disparate code paths each of which contribute to
- * maintaining continuous, working Internet connectivity.
- *
- * @hide
- */
-public class MultinetworkPolicyTracker {
-    private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
-
-    private final Context mContext;
-    private final ConnectivityResources mResources;
-    private final Handler mHandler;
-    private final Runnable mAvoidBadWifiCallback;
-    private final List<Uri> mSettingsUris;
-    private final ContentResolver mResolver;
-    private final SettingObserver mSettingObserver;
-    private final BroadcastReceiver mBroadcastReceiver;
-
-    private volatile boolean mAvoidBadWifi = true;
-    private volatile int mMeteredMultipathPreference;
-    private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private volatile long mTestAllowBadWifiUntilMs = 0;
-
-    // Mainline module can't use internal HandlerExecutor, so add an identical executor here.
-    private static class HandlerExecutor implements Executor {
-        @NonNull
-        private final Handler mHandler;
-
-        HandlerExecutor(@NonNull Handler handler) {
-            mHandler = handler;
-        }
-        @Override
-        public void execute(Runnable command) {
-            if (!mHandler.post(command)) {
-                throw new RejectedExecutionException(mHandler + " is shutting down");
-            }
-        }
-    }
-    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
-    @VisibleForTesting @TargetApi(Build.VERSION_CODES.S)
-    protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
-            implements TelephonyCallback.ActiveDataSubscriptionIdListener {
-        @Override
-        public void onActiveDataSubscriptionIdChanged(int subId) {
-            mActiveSubId = subId;
-            reevaluateInternal();
-        }
-    }
-
-    public MultinetworkPolicyTracker(Context ctx, Handler handler) {
-        this(ctx, handler, null);
-    }
-
-    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
-    @TargetApi(Build.VERSION_CODES.S)
-    public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
-        mContext = ctx;
-        mResources = new ConnectivityResources(ctx);
-        mHandler = handler;
-        mAvoidBadWifiCallback = avoidBadWifiCallback;
-        mSettingsUris = Arrays.asList(
-                Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
-                Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
-        mResolver = mContext.getContentResolver();
-        mSettingObserver = new SettingObserver();
-        mBroadcastReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                reevaluateInternal();
-            }
-        };
-
-        ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback(
-                new HandlerExecutor(handler), new ActiveDataSubscriptionIdListener());
-
-        updateAvoidBadWifi();
-        updateMeteredMultipathPreference();
-    }
-
-    public void start() {
-        for (Uri uri : mSettingsUris) {
-            mResolver.registerContentObserver(uri, false, mSettingObserver);
-        }
-
-        final IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter,
-                null /* broadcastPermission */, mHandler);
-
-        reevaluate();
-    }
-
-    public void shutdown() {
-        mResolver.unregisterContentObserver(mSettingObserver);
-
-        mContext.unregisterReceiver(mBroadcastReceiver);
-    }
-
-    public boolean getAvoidBadWifi() {
-        return mAvoidBadWifi;
-    }
-
-    // TODO: move this to MultipathPolicyTracker.
-    public int getMeteredMultipathPreference() {
-        return mMeteredMultipathPreference;
-    }
-
-    /**
-     * Whether the device or carrier configuration disables avoiding bad wifi by default.
-     */
-    public boolean configRestrictsAvoidBadWifi() {
-        final boolean allowBadWifi = mTestAllowBadWifiUntilMs > 0
-                && mTestAllowBadWifiUntilMs > System.currentTimeMillis();
-        // If the config returns true, then avoid bad wifi design can be controlled by the
-        // NETWORK_AVOID_BAD_WIFI setting.
-        if (allowBadWifi) return true;
-
-        // TODO: use R.integer.config_networkAvoidBadWifi directly
-        final int id = mResources.get().getIdentifier("config_networkAvoidBadWifi",
-                "integer", mResources.getResourcesContext().getPackageName());
-        return (getResourcesForActiveSubId().getInteger(id) == 0);
-    }
-
-    /**
-     * Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
-     * The value works when the time set is more than {@link System.currentTimeMillis()}.
-     */
-    public void setTestAllowBadWifiUntil(long timeMs) {
-        Log.d(TAG, "setTestAllowBadWifiUntil: " + timeMs);
-        mTestAllowBadWifiUntilMs = timeMs;
-        reevaluateInternal();
-    }
-
-    @VisibleForTesting
-    @NonNull
-    protected Resources getResourcesForActiveSubId() {
-        return SubscriptionManager.getResourcesForSubId(
-                mResources.getResourcesContext(), mActiveSubId);
-    }
-
-    /**
-     * Whether we should display a notification when wifi becomes unvalidated.
-     */
-    public boolean shouldNotifyWifiUnvalidated() {
-        return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
-    }
-
-    public String getAvoidBadWifiSetting() {
-        return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
-    }
-
-    @VisibleForTesting
-    public void reevaluate() {
-        mHandler.post(this::reevaluateInternal);
-    }
-
-    /**
-     * Reevaluate the settings. Must be called on the handler thread.
-     */
-    private void reevaluateInternal() {
-        if (updateAvoidBadWifi() && mAvoidBadWifiCallback != null) {
-            mAvoidBadWifiCallback.run();
-        }
-        updateMeteredMultipathPreference();
-    }
-
-    public boolean updateAvoidBadWifi() {
-        final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
-        final boolean prev = mAvoidBadWifi;
-        mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
-        return mAvoidBadWifi != prev;
-    }
-
-    /**
-     * The default (device and carrier-dependent) value for metered multipath preference.
-     */
-    public int configMeteredMultipathPreference() {
-        // TODO: use R.integer.config_networkMeteredMultipathPreference directly
-        final int id = mResources.get().getIdentifier("config_networkMeteredMultipathPreference",
-                "integer", mResources.getResourcesContext().getPackageName());
-        return mResources.get().getInteger(id);
-    }
-
-    public void updateMeteredMultipathPreference() {
-        String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
-        try {
-            mMeteredMultipathPreference = Integer.parseInt(setting);
-        } catch (NumberFormatException e) {
-            mMeteredMultipathPreference = configMeteredMultipathPreference();
-        }
-    }
-
-    private class SettingObserver extends ContentObserver {
-        public SettingObserver() {
-            super(null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            Log.wtf(TAG, "Should never be reached.");
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (!mSettingsUris.contains(uri)) {
-                Log.wtf(TAG, "Unexpected settings observation: " + uri);
-            }
-            reevaluate();
-        }
-    }
-}
diff --git a/nearby/README.md b/nearby/README.md
index 6925dc4..8451882 100644
--- a/nearby/README.md
+++ b/nearby/README.md
@@ -29,6 +29,20 @@
 $ aidegen .
 # This will launch Intellij project for Nearby module.
 ```
+Note, the setup above may fail to index classes defined in proto, such
+that all classes defined in proto shows red in IDE and cannot be auto-completed.
+To fix, you can mannually add jar files generated from proto to the class path
+as below.  First, find the jar file of presence proto with
+```sh
+ls $ANDROID_BUILD_TOP/out/soong/.intermediates/packages/modules/Connectivity/nearby/service/proto/presence-lite-protos/android_common/combined/presence-lite-protos.jar
+```
+Then, add the jar in IDE as below.
+1. Menu: File > Project Structure
+2. Select Modules at the left panel and select the Dependencies tab.
+3. Select the + icon and select 1 JARs or Directories option.
+4. Select the JAR file found above, make sure it is checked in the beginning square.
+5. Click the OK button.
+6. Restart the IDE to re-index.
 
 ## Build and Install
 
@@ -40,3 +54,23 @@
     --output /tmp/tethering.apex
 $ adb install -r /tmp/tethering.apex
 ```
+
+## Build and Install from tm-mainline-prod branch
+When build and flash the APEX from tm-mainline-prod, you may see the error below.
+```
+[INSTALL_FAILED_VERSION_DOWNGRADE: Downgrade of APEX package com.google.android.tethering is not allowed. Active version: 990090000 attempted: 339990000])
+```
+This is because the device is flashed with AOSP built from master or other branches, which has
+prebuilt APEX with higher version. We can use root access to replace the prebuilt APEX with the APEX
+built from tm-mainline-prod as below.
+1. adb root && adb remount; adb shell mount -orw,remount /system/apex
+2. cp tethering.next.apex com.google.android.tethering.apex
+3. adb push  com.google.android.tethering.apex  /system/apex/
+4. adb reboot
+After the steps above, the APEX can be reinstalled with adb install -r.
+(More APEX background can be found in https://source.android.com/docs/core/ota/apex#using-an-apex.)
+
+## Build APEX to support multiple platforms
+If you need to flash the APEX to different devices, Pixel 6, Pixel 7, or even devices from OEM, you
+can share the APEX by ```source build/envsetup.sh && lunch aosp_arm64-userdebug```. This can avoid
+ re-compiling for different targets.
diff --git a/nearby/framework/java/android/nearby/DataElement.java b/nearby/framework/java/android/nearby/DataElement.java
index 6fa5fb5..0aa3499 100644
--- a/nearby/framework/java/android/nearby/DataElement.java
+++ b/nearby/framework/java/android/nearby/DataElement.java
@@ -16,13 +16,17 @@
 
 package android.nearby;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Represents a data element in Nearby Presence.
@@ -35,11 +39,76 @@
     private final int mKey;
     private final byte[] mValue;
 
+    /** @hide */
+    @IntDef({
+            DataType.BLE_SERVICE_DATA,
+            DataType.BLE_ADDRESS,
+            DataType.SALT,
+            DataType.PRIVATE_IDENTITY,
+            DataType.TRUSTED_IDENTITY,
+            DataType.PUBLIC_IDENTITY,
+            DataType.PROVISIONED_IDENTITY,
+            DataType.TX_POWER,
+            DataType.ACTION,
+            DataType.MODEL_ID,
+            DataType.EDDYSTONE_EPHEMERAL_IDENTIFIER,
+            DataType.ACCOUNT_KEY_DATA,
+            DataType.CONNECTION_STATUS,
+            DataType.BATTERY
+    })
+    public @interface DataType {
+        int BLE_SERVICE_DATA = 100;
+        int BLE_ADDRESS = 101;
+        int SALT = 0;
+        int PRIVATE_IDENTITY = 1;
+        int TRUSTED_IDENTITY = 2;
+        int PUBLIC_IDENTITY = 3;
+        int PROVISIONED_IDENTITY = 4;
+        int TX_POWER = 5;
+        int ACTION = 6;
+        int MODEL_ID = 7;
+        int EDDYSTONE_EPHEMERAL_IDENTIFIER = 8;
+        int ACCOUNT_KEY_DATA = 9;
+        int CONNECTION_STATUS = 10;
+        int BATTERY = 11;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isValidType(int type) {
+        return type == DataType.BLE_SERVICE_DATA
+                || type == DataType.ACCOUNT_KEY_DATA
+                || type == DataType.BLE_ADDRESS
+                || type == DataType.SALT
+                || type == DataType.PRIVATE_IDENTITY
+                || type == DataType.TRUSTED_IDENTITY
+                || type == DataType.PUBLIC_IDENTITY
+                || type == DataType.PROVISIONED_IDENTITY
+                || type == DataType.TX_POWER
+                || type == DataType.ACTION
+                || type == DataType.MODEL_ID
+                || type == DataType.EDDYSTONE_EPHEMERAL_IDENTIFIER
+                || type == DataType.CONNECTION_STATUS
+                || type == DataType.BATTERY;
+    }
+
+    /**
+     * @return {@code true} if this is identity type.
+     * @hide
+     */
+    public boolean isIdentityDataType() {
+        return mKey == DataType.PRIVATE_IDENTITY
+                || mKey == DataType.TRUSTED_IDENTITY
+                || mKey == DataType.PUBLIC_IDENTITY
+                || mKey == DataType.PROVISIONED_IDENTITY;
+    }
+
     /**
      * Constructs a {@link DataElement}.
      */
     public DataElement(int key, @NonNull byte[] value) {
-        Preconditions.checkState(value != null, "value cannot be null");
+        Preconditions.checkArgument(value != null, "value cannot be null");
         mKey = key;
         mValue = value;
     }
@@ -61,6 +130,20 @@
     };
 
     @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj instanceof DataElement) {
+            return mKey == ((DataElement) obj).mKey
+                    && Arrays.equals(mValue, ((DataElement) obj).mValue);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mKey, Arrays.hashCode(mValue));
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/nearby/framework/java/android/nearby/NearbyDevice.java b/nearby/framework/java/android/nearby/NearbyDevice.java
index 538940c..e8fcc28 100644
--- a/nearby/framework/java/android/nearby/NearbyDevice.java
+++ b/nearby/framework/java/android/nearby/NearbyDevice.java
@@ -21,11 +21,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.util.ArraySet;
 
 import com.android.internal.util.Preconditions;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * A class represents a device that can be discovered by multiple mediums.
@@ -123,13 +125,17 @@
 
     @Override
     public boolean equals(Object other) {
-        if (other instanceof NearbyDevice) {
-            NearbyDevice otherDevice = (NearbyDevice) other;
-            return Objects.equals(mName, otherDevice.mName)
-                    && mMediums == otherDevice.mMediums
-                    && mRssi == otherDevice.mRssi;
+        if (!(other instanceof NearbyDevice)) {
+            return false;
         }
-        return false;
+        NearbyDevice otherDevice = (NearbyDevice) other;
+        Set<Integer> mediumSet = new ArraySet<>(mMediums);
+        Set<Integer> otherMediumSet = new ArraySet<>(otherDevice.mMediums);
+        if (!mediumSet.equals(otherMediumSet)) {
+            return false;
+        }
+
+        return Objects.equals(mName, otherDevice.mName) && mRssi == otherDevice.mRssi;
     }
 
     @Override
diff --git a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
index 8f44091..180b662 100644
--- a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
+++ b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
@@ -76,6 +76,17 @@
                         in.readByteArray(salt);
                         builder.setData(salt);
                     }
+                    if (in.readInt() == 1) {
+                        builder.setPresenceDevice(in.readParcelable(
+                                PresenceDevice.class.getClassLoader(),
+                                PresenceDevice.class));
+                    }
+                    if (in.readInt() == 1) {
+                        int encryptionKeyTagLength = in.readInt();
+                        byte[] keyTag = new byte[encryptionKeyTagLength];
+                        in.readByteArray(keyTag);
+                        builder.setData(keyTag);
+                    }
                     return builder.build();
                 }
 
@@ -96,6 +107,8 @@
     @Nullable private final String mFastPairModelId;
     @Nullable private final byte[] mData;
     @Nullable private final byte[] mSalt;
+    @Nullable private final PresenceDevice mPresenceDevice;
+    @Nullable private final byte[] mEncryptionKeyTag;
 
     private NearbyDeviceParcelable(
             @ScanRequest.ScanType int scanType,
@@ -108,7 +121,9 @@
             @Nullable String fastPairModelId,
             @Nullable String bluetoothAddress,
             @Nullable byte[] data,
-            @Nullable byte[] salt) {
+            @Nullable byte[] salt,
+            @Nullable PresenceDevice presenceDevice,
+            @Nullable byte[] encryptionKeyTag) {
         mScanType = scanType;
         mName = name;
         mMedium = medium;
@@ -120,6 +135,8 @@
         mBluetoothAddress = bluetoothAddress;
         mData = data;
         mSalt = salt;
+        mPresenceDevice = presenceDevice;
+        mEncryptionKeyTag = encryptionKeyTag;
     }
 
     /** No special parcel contents. */
@@ -164,6 +181,15 @@
             dest.writeInt(mSalt.length);
             dest.writeByteArray(mSalt);
         }
+        dest.writeInt(mPresenceDevice == null ? 0 : 1);
+        if (mPresenceDevice != null) {
+            dest.writeParcelable(mPresenceDevice, /* parcelableFlags= */ 0);
+        }
+        dest.writeInt(mEncryptionKeyTag == null ? 0 : 1);
+        if (mEncryptionKeyTag != null) {
+            dest.writeInt(mEncryptionKeyTag.length);
+            dest.writeByteArray(mEncryptionKeyTag);
+        }
     }
 
     /** Returns a string representation of this ScanRequest. */
@@ -210,7 +236,11 @@
                     && (Objects.equals(
                             mFastPairModelId, otherNearbyDeviceParcelable.mFastPairModelId))
                     && (Arrays.equals(mData, otherNearbyDeviceParcelable.mData))
-                    && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt));
+                    && (Arrays.equals(mSalt, otherNearbyDeviceParcelable.mSalt))
+                    && (Objects.equals(
+                            mPresenceDevice, otherNearbyDeviceParcelable.mPresenceDevice))
+                    && (Arrays.equals(
+                            mEncryptionKeyTag, otherNearbyDeviceParcelable.mEncryptionKeyTag));
         }
         return false;
     }
@@ -227,7 +257,9 @@
                 mBluetoothAddress,
                 mFastPairModelId,
                 Arrays.hashCode(mData),
-                Arrays.hashCode(mSalt));
+                Arrays.hashCode(mSalt),
+                mPresenceDevice,
+                Arrays.hashCode(mEncryptionKeyTag));
     }
 
     /**
@@ -351,6 +383,26 @@
         return mSalt;
     }
 
+    /**
+     * Gets the {@link PresenceDevice} Nearby Presence device. This field is
+     * for Fast Pair client only.
+     */
+    @Nullable
+    public PresenceDevice getPresenceDevice() {
+        return mPresenceDevice;
+    }
+
+    /**
+     * Gets the encryption key tag calculated from advertisement
+     * Returns {@code null} if the data is not encrypted or this is not a Presence device.
+     *
+     * Used in Presence.
+     */
+    @Nullable
+    public byte[] getEncryptionKeyTag() {
+        return mEncryptionKeyTag;
+    }
+
     /** Builder class for {@link NearbyDeviceParcelable}. */
     public static final class Builder {
         @Nullable private String mName;
@@ -364,6 +416,8 @@
         @Nullable private String mBluetoothAddress;
         @Nullable private byte[] mData;
         @Nullable private byte[] mSalt;
+        @Nullable private PresenceDevice mPresenceDevice;
+        @Nullable private byte[] mEncryptionKeyTag;
 
         /**
          * Sets the scan type of the NearbyDeviceParcelable.
@@ -469,7 +523,7 @@
         /**
          * Sets the scanned raw data.
          *
-         * @param data Data the scan. For example, {@link ScanRecord#getServiceData()} if scanned by
+         * @param data raw data scanned, like {@link ScanRecord#getServiceData()} if scanned by
          *             Bluetooth.
          */
         @NonNull
@@ -479,6 +533,17 @@
         }
 
         /**
+         * Sets the encryption key tag calculated from the advertisement.
+         *
+         * @param encryptionKeyTag calculated from identity scanned from the advertisement
+         */
+        @NonNull
+        public Builder setEncryptionKeyTag(@Nullable byte[] encryptionKeyTag) {
+            mEncryptionKeyTag = encryptionKeyTag;
+            return this;
+        }
+
+        /**
          * Sets the slat in the advertisement from the Nearby Presence device.
          *
          * @param salt in the advertisement from the Nearby Presence device.
@@ -489,6 +554,17 @@
             return this;
         }
 
+        /**
+         * Sets the {@link PresenceDevice} if there is any.
+         * The current {@link NearbyDeviceParcelable} can be seen as the wrapper of the
+         * {@link PresenceDevice}.
+         */
+        @Nullable
+        public Builder setPresenceDevice(@Nullable PresenceDevice presenceDevice) {
+            mPresenceDevice = presenceDevice;
+            return this;
+        }
+
         /** Builds a ScanResult. */
         @NonNull
         public NearbyDeviceParcelable build() {
@@ -503,7 +579,9 @@
                     mFastPairModelId,
                     mBluetoothAddress,
                     mData,
-                    mSalt);
+                    mSalt,
+                    mPresenceDevice,
+                    mEncryptionKeyTag);
         }
     }
 }
diff --git a/nearby/framework/java/android/nearby/NearbyManager.java b/nearby/framework/java/android/nearby/NearbyManager.java
index 106c290..595f62e 100644
--- a/nearby/framework/java/android/nearby/NearbyManager.java
+++ b/nearby/framework/java/android/nearby/NearbyManager.java
@@ -103,6 +103,7 @@
         mService = service;
     }
 
+    @Nullable
     private static NearbyDevice toClientNearbyDevice(
             NearbyDeviceParcelable nearbyDeviceParcelable,
             @ScanRequest.ScanType int scanType) {
@@ -118,23 +119,12 @@
         }
 
         if (scanType == ScanRequest.SCAN_TYPE_NEARBY_PRESENCE) {
-            PublicCredential publicCredential = nearbyDeviceParcelable.getPublicCredential();
-            if (publicCredential == null) {
-                return null;
+            PresenceDevice presenceDevice = nearbyDeviceParcelable.getPresenceDevice();
+            if (presenceDevice == null) {
+                Log.e(TAG,
+                        "Cannot find any Presence device in discovered NearbyDeviceParcelable");
             }
-            byte[] salt = nearbyDeviceParcelable.getSalt();
-            if (salt == null) {
-                salt = new byte[0];
-            }
-            return new PresenceDevice.Builder(
-                    // Use the public credential hash as the device Id.
-                    String.valueOf(publicCredential.hashCode()),
-                    salt,
-                    publicCredential.getSecretId(),
-                    publicCredential.getEncryptedMetadata())
-                    .setRssi(nearbyDeviceParcelable.getRssi())
-                    .addMedium(nearbyDeviceParcelable.getMedium())
-                    .build();
+            return presenceDevice;
         }
         return null;
     }
@@ -339,9 +329,9 @@
         public void onDiscovered(NearbyDeviceParcelable nearbyDeviceParcelable)
                 throws RemoteException {
             mExecutor.execute(() -> {
-                if (mScanCallback != null) {
-                    mScanCallback.onDiscovered(
-                            toClientNearbyDevice(nearbyDeviceParcelable, mScanType));
+                NearbyDevice nearbyDevice = toClientNearbyDevice(nearbyDeviceParcelable, mScanType);
+                if (mScanCallback != null && nearbyDevice != null) {
+                    mScanCallback.onDiscovered(nearbyDevice);
                 }
             });
         }
@@ -350,7 +340,8 @@
         public void onUpdated(NearbyDeviceParcelable nearbyDeviceParcelable)
                 throws RemoteException {
             mExecutor.execute(() -> {
-                if (mScanCallback != null) {
+                NearbyDevice nearbyDevice = toClientNearbyDevice(nearbyDeviceParcelable, mScanType);
+                if (mScanCallback != null && nearbyDevice != null) {
                     mScanCallback.onUpdated(
                             toClientNearbyDevice(nearbyDeviceParcelable, mScanType));
                 }
@@ -360,7 +351,8 @@
         @Override
         public void onLost(NearbyDeviceParcelable nearbyDeviceParcelable) throws RemoteException {
             mExecutor.execute(() -> {
-                if (mScanCallback != null) {
+                NearbyDevice nearbyDevice = toClientNearbyDevice(nearbyDeviceParcelable, mScanType);
+                if (mScanCallback != null && nearbyDevice != null) {
                     mScanCallback.onLost(
                             toClientNearbyDevice(nearbyDeviceParcelable, mScanType));
                 }
diff --git a/nearby/framework/java/android/nearby/PresenceDevice.java b/nearby/framework/java/android/nearby/PresenceDevice.java
index cb406e4..7efa5e6 100644
--- a/nearby/framework/java/android/nearby/PresenceDevice.java
+++ b/nearby/framework/java/android/nearby/PresenceDevice.java
@@ -26,6 +26,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -134,6 +135,54 @@
         return mExtendedProperties;
     }
 
+    /**
+     * This can only be hidden because this is the System API,
+     * which cannot be changed in T timeline.
+     *
+     * @hide
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof PresenceDevice) {
+            PresenceDevice otherDevice = (PresenceDevice) other;
+            if (super.equals(otherDevice)) {
+                return Arrays.equals(mSalt, otherDevice.mSalt)
+                        && Arrays.equals(mSecretId, otherDevice.mSecretId)
+                        && Arrays.equals(mEncryptedIdentity, otherDevice.mEncryptedIdentity)
+                        && Objects.equals(mDeviceId, otherDevice.mDeviceId)
+                        && mDeviceType == otherDevice.mDeviceType
+                        && Objects.equals(mDeviceImageUrl, otherDevice.mDeviceImageUrl)
+                        && mDiscoveryTimestampMillis == otherDevice.mDiscoveryTimestampMillis
+                        && Objects.equals(mExtendedProperties, otherDevice.mExtendedProperties);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This can only be hidden because this is the System API,
+     * which cannot be changed in T timeline.
+     *
+     * @hide
+     *
+     * @return The unique hash value of the {@link PresenceDevice}
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                getName(),
+                getMediums(),
+                getRssi(),
+                Arrays.hashCode(mSalt),
+                Arrays.hashCode(mSecretId),
+                Arrays.hashCode(mEncryptedIdentity),
+                mDeviceId,
+                mDeviceType,
+                mDeviceImageUrl,
+                mDiscoveryTimestampMillis,
+                mExtendedProperties);
+    }
+
     private PresenceDevice(String deviceName, List<Integer> mMediums, int rssi, String deviceId,
             byte[] salt, byte[] secretId, byte[] encryptedIdentity, int deviceType,
             String deviceImageUrl, long discoveryTimestampMillis,
diff --git a/nearby/framework/java/android/nearby/PresenceScanFilter.java b/nearby/framework/java/android/nearby/PresenceScanFilter.java
index f0c3c06..50e97b4 100644
--- a/nearby/framework/java/android/nearby/PresenceScanFilter.java
+++ b/nearby/framework/java/android/nearby/PresenceScanFilter.java
@@ -71,7 +71,7 @@
         super(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE, rssiThreshold);
         mCredentials = new ArrayList<>(credentials);
         mPresenceActions = new ArrayList<>(presenceActions);
-        mExtendedProperties = extendedProperties;
+        mExtendedProperties = new ArrayList<>(extendedProperties);
     }
 
     private PresenceScanFilter(Parcel in) {
@@ -132,7 +132,7 @@
         }
         dest.writeInt(mExtendedProperties.size());
         if (!mExtendedProperties.isEmpty()) {
-            dest.writeList(mExtendedProperties);
+            dest.writeParcelableList(mExtendedProperties, 0);
         }
     }
 
diff --git a/nearby/halfsheet/Android.bp b/nearby/halfsheet/Android.bp
index 486a3ff..2d0d327 100644
--- a/nearby/halfsheet/Android.bp
+++ b/nearby/halfsheet/Android.bp
@@ -23,7 +23,6 @@
     sdk_version: "module_current",
     // This is included in tethering apex, which uses min SDK 30
     min_sdk_version: "30",
-    target_sdk_version: "current",
     updatable: true,
     certificate: ":com.android.nearby.halfsheetcertificate",
     libs: [
@@ -44,7 +43,6 @@
         "com.google.android.material_material",
         "fast-pair-lite-protos",
     ],
-    plugins: ["java_api_finder"],
     manifest: "AndroidManifest.xml",
     jarjar_rules: ":nearby-jarjar-rules",
     apex_available: ["com.android.tethering",],
diff --git a/nearby/halfsheet/res/values-en-rCA/strings.xml b/nearby/halfsheet/res/values-en-rCA/strings.xml
index d4ed675..6094199 100644
--- a/nearby/halfsheet/res/values-en-rCA/strings.xml
+++ b/nearby/halfsheet/res/values-en-rCA/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="fast_pair_setup_in_progress" msgid="4158762239172829807">"Starting setup…"</string>
+    <string name="fast_pair_setup_in_progress" msgid="4158762239172829807">"Starting Setup…"</string>
     <string name="fast_pair_title_setup" msgid="2894360355540593246">"Set up device"</string>
     <string name="fast_pair_device_ready" msgid="2903490346082833101">"Device connected"</string>
     <string name="fast_pair_title_fail" msgid="5677174346601290232">"Couldn\'t connect"</string>
diff --git a/nearby/halfsheet/res/values-ro/strings.xml b/nearby/halfsheet/res/values-ro/strings.xml
index 5b50f15..189f698 100644
--- a/nearby/halfsheet/res/values-ro/strings.xml
+++ b/nearby/halfsheet/res/values-ro/strings.xml
@@ -18,12 +18,12 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="fast_pair_setup_in_progress" msgid="4158762239172829807">"Începe configurarea…"</string>
-    <string name="fast_pair_title_setup" msgid="2894360355540593246">"Configurați dispozitivul"</string>
+    <string name="fast_pair_title_setup" msgid="2894360355540593246">"Configurează dispozitivul"</string>
     <string name="fast_pair_device_ready" msgid="2903490346082833101">"Dispozitivul s-a conectat"</string>
     <string name="fast_pair_title_fail" msgid="5677174346601290232">"Nu s-a putut conecta"</string>
     <string name="paring_action_done" msgid="6888875159174470731">"Gata"</string>
-    <string name="paring_action_save" msgid="6259357442067880136">"Salvați"</string>
-    <string name="paring_action_connect" msgid="4801102939608129181">"Conectați"</string>
-    <string name="paring_action_launch" msgid="8940808384126591230">"Configurați"</string>
+    <string name="paring_action_save" msgid="6259357442067880136">"Salvează"</string>
+    <string name="paring_action_connect" msgid="4801102939608129181">"Conectează"</string>
+    <string name="paring_action_launch" msgid="8940808384126591230">"Configurează"</string>
     <string name="paring_action_settings" msgid="424875657242864302">"Setări"</string>
 </resources>
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
index 2a38b8a..07e5776 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.nearby.halfsheet;
 
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
 import static com.android.nearby.halfsheet.fragment.DevicePairingFragment.APP_LAUNCH_FRAGMENT_TYPE;
 import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID;
 import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_MAC_ADDRESS;
@@ -226,7 +228,8 @@
                                     EXTRA_HALF_SHEET_IS_RETROACTIVE,
                                     getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE,
                                             false))
-                            .putExtra(EXTRA_MAC_ADDRESS, mScanFastPairStoreItem.getAddress()));
+                            .putExtra(EXTRA_MAC_ADDRESS, mScanFastPairStoreItem.getAddress()),
+                    ACCESS_FINE_LOCATION);
         }
     }
 
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
index 467997c..2f1e90a 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
@@ -31,6 +31,13 @@
         context.sendBroadcast(intent);
     }
 
+    /**
+     * Helps send a broadcast with specified receiver permission.
+     */
+    public static void sendBroadcast(Context context, Intent intent, String receiverPermission) {
+        context.sendBroadcast(intent, receiverPermission);
+    }
+
     private BroadcastUtils() {
     }
 }
diff --git a/nearby/service/Android.bp b/nearby/service/Android.bp
index d318a80..ef07bb9 100644
--- a/nearby/service/Android.bp
+++ b/nearby/service/Android.bp
@@ -85,10 +85,11 @@
     ],
     libs: [
         "androidx.annotation_annotation",
-        "framework-bluetooth.stubs.module_lib", // TODO(b/215722418): Change to framework-bluetooth once fixed
+        "framework-bluetooth",
         "error_prone_annotations",
+        "framework-configinfrastructure",
         "framework-connectivity-t.impl",
-        "framework-statsd.stubs.module_lib",
+        "framework-statsd",
     ],
     static_libs: [
         "androidx.core_core",
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 5ebf1e5..be3b84e 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.hardware.location.ContextHubManager;
 import android.nearby.BroadcastRequestParcelable;
 import android.nearby.IBroadcastListener;
@@ -41,8 +42,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.FastPairManager;
-import com.android.server.nearby.injector.ContextHubManagerAdapter;
 import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.PresenceManager;
 import com.android.server.nearby.provider.BroadcastProviderManager;
 import com.android.server.nearby.provider.DiscoveryProviderManager;
 import com.android.server.nearby.provider.FastPairDataProvider;
@@ -53,10 +54,16 @@
 /** Service implementing nearby functionality. */
 public class NearbyService extends INearbyManager.Stub {
     public static final String TAG = "NearbyService";
+    // Sets to true to start BLE scan from PresenceManager for manual testing.
+    public static final Boolean MANUAL_TEST = false;
+    // Sets to true to support Mainline Test App.
+    // This will disable BLE privilege check and legacy broadcast support check.
+    public static final Boolean SUPPORT_TEST_APP = false;
 
     private final Context mContext;
     private Injector mInjector;
     private final FastPairManager mFastPairManager;
+    private final PresenceManager mPresenceManager;
     private final BroadcastReceiver mBluetoothReceiver =
             new BroadcastReceiver() {
                 @Override
@@ -84,6 +91,7 @@
         mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
         final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
         mFastPairManager = new FastPairManager(lcw);
+        mPresenceManager = new PresenceManager(lcw);
     }
 
     @VisibleForTesting
@@ -157,16 +165,22 @@
                 FastPairDataProvider.init(mContext);
                 break;
             case PHASE_BOOT_COMPLETED:
+                // mInjector needs to be initialized before mProviderManager.
                 if (mInjector instanceof SystemInjector) {
                     // The nearby service must be functioning after this boot phase.
                     ((SystemInjector) mInjector).initializeBluetoothAdapter();
                     // Initialize ContextManager for CHRE scan.
-                    ((SystemInjector) mInjector).initializeContextHubManagerAdapter();
+                    ((SystemInjector) mInjector).initializeContextHubManager();
                 }
+                mProviderManager.init();
                 mContext.registerReceiver(
                         mBluetoothReceiver,
                         new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
                 mFastPairManager.initiate();
+                // Only enable for manual Presence test on device.
+                if (MANUAL_TEST) {
+                    mPresenceManager.initiate();
+                }
                 break;
         }
     }
@@ -178,15 +192,17 @@
      */
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
     private static void enforceBluetoothPrivilegedPermission(Context context) {
-        context.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-                "Need BLUETOOTH PRIVILEGED permission");
+        if (!SUPPORT_TEST_APP) {
+            context.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+                    "Need BLUETOOTH PRIVILEGED permission");
+        }
     }
 
     private static final class SystemInjector implements Injector {
         private final Context mContext;
         @Nullable private BluetoothAdapter mBluetoothAdapter;
-        @Nullable private ContextHubManagerAdapter mContextHubManagerAdapter;
+        @Nullable private ContextHubManager mContextHubManager;
         @Nullable private AppOpsManager mAppOpsManager;
 
         SystemInjector(Context context) {
@@ -201,8 +217,8 @@
 
         @Override
         @Nullable
-        public ContextHubManagerAdapter getContextHubManagerAdapter() {
-            return mContextHubManagerAdapter;
+        public ContextHubManager getContextHubManager() {
+            return mContextHubManager;
         }
 
         @Override
@@ -222,15 +238,13 @@
             mBluetoothAdapter = manager.getAdapter();
         }
 
-        synchronized void initializeContextHubManagerAdapter() {
-            if (mContextHubManagerAdapter != null) {
+        synchronized void initializeContextHubManager() {
+            if (mContextHubManager != null) {
                 return;
             }
-            ContextHubManager manager = mContext.getSystemService(ContextHubManager.class);
-            if (manager == null) {
-                return;
+            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) {
+                mContextHubManager = mContext.getSystemService(ContextHubManager.class);
             }
-            mContextHubManagerAdapter = new ContextHubManagerAdapter(manager);
         }
 
         synchronized void initializeAppOpsManager() {
diff --git a/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java b/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java
index 23d5170..dc4e11e 100644
--- a/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java
+++ b/nearby/service/java/com/android/server/nearby/common/ble/BleFilter.java
@@ -500,16 +500,16 @@
             return false;
         }
         BleFilter other = (BleFilter) obj;
-        return mDeviceName.equals(other.mDeviceName)
-                && mDeviceAddress.equals(other.mDeviceAddress)
+        return equal(mDeviceName, other.mDeviceName)
+                && equal(mDeviceAddress, other.mDeviceAddress)
                 && mManufacturerId == other.mManufacturerId
                 && Arrays.equals(mManufacturerData, other.mManufacturerData)
                 && Arrays.equals(mManufacturerDataMask, other.mManufacturerDataMask)
-                && mServiceDataUuid.equals(other.mServiceDataUuid)
+                && equal(mServiceDataUuid, other.mServiceDataUuid)
                 && Arrays.equals(mServiceData, other.mServiceData)
                 && Arrays.equals(mServiceDataMask, other.mServiceDataMask)
-                && mServiceUuid.equals(other.mServiceUuid)
-                && mServiceUuidMask.equals(other.mServiceUuidMask);
+                && equal(mServiceUuid, other.mServiceUuid)
+                && equal(mServiceUuidMask, other.mServiceUuidMask);
     }
 
     /** Builder class for {@link BleFilter}. */
@@ -743,4 +743,11 @@
         }
         return osFilterBuilder.build();
     }
+
+    /**
+     * equal() method for two possibly-null objects
+     */
+    private static boolean equal(@Nullable Object obj1, @Nullable Object obj2) {
+        return obj1 == obj2 || (obj1 != null && obj1.equals(obj2));
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java b/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java
index f27899f..b4f46f8 100644
--- a/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java
+++ b/nearby/service/java/com/android/server/nearby/common/ble/testing/FastPairTestData.java
@@ -46,6 +46,12 @@
     /** Model ID in {@link #getFastPairRecord()}. */
     public static final byte[] FAST_PAIR_MODEL_ID = Hex.stringToBytes("AABBCC");
 
+    /** An arbitrary BLE device address. */
+    public static final String DEVICE_ADDRESS = "00:00:00:00:00:01";
+
+    /** Arbitrary RSSI (Received Signal Strength Indicator). */
+    public static final int RSSI = -72;
+
     /** @see #getFastPairRecord() */
     public static byte[] newFastPairRecord(byte header, byte[] modelId) {
         return newFastPairRecord(
@@ -61,6 +67,45 @@
                         Hex.bytesToStringUppercase(serviceData)));
     }
 
+    // This is an example extended inquiry response for a phone with PANU
+    // and Hands-free Audio Gateway
+    public static byte[] eir_1 = {
+            0x06, // Length of this Data
+            0x09, // <<Complete Local Name>>
+            'P',
+            'h',
+            'o',
+            'n',
+            'e',
+            0x05, // Length of this Data
+            0x03, // <<Complete list of 16-bit Service UUIDs>>
+            0x15,
+            0x11, // PANU service class UUID
+            0x1F,
+            0x11, // Hands-free Audio Gateway service class UUID
+            0x01, // Length of this data
+            0x05, // <<Complete list of 32-bit Service UUIDs>>
+            0x11, // Length of this data
+            0x07, // <<Complete list of 128-bit Service UUIDs>>
+            0x01,
+            0x02,
+            0x03,
+            0x04,
+            0x05,
+            0x06,
+            0x07,
+            0x08, // Made up UUID
+            0x11,
+            0x12,
+            0x13,
+            0x14,
+            0x15,
+            0x16,
+            0x17,
+            0x18, //
+            0x00 // End of Data (Not transmitted over the air
+    };
+
     // This is an example of advertising data with AD types
     public static byte[] adv_1 = {
             0x02, // Length of this Data
@@ -138,4 +183,46 @@
             0x00
     };
 
+    // An Eddystone UID frame. go/eddystone for more info
+    public static byte[] eddystone_header_and_uuid = {
+            // BLE Flags
+            (byte) 0x02,
+            (byte) 0x01,
+            (byte) 0x06,
+            // Service UUID
+            (byte) 0x03,
+            (byte) 0x03,
+            (byte) 0xaa,
+            (byte) 0xfe,
+            // Service data header
+            (byte) 0x17,
+            (byte) 0x16,
+            (byte) 0xaa,
+            (byte) 0xfe,
+            // Eddystone frame type
+            (byte) 0x00,
+            // Ranging data
+            (byte) 0xb3,
+            // Eddystone ID namespace
+            (byte) 0x0a,
+            (byte) 0x09,
+            (byte) 0x08,
+            (byte) 0x07,
+            (byte) 0x06,
+            (byte) 0x05,
+            (byte) 0x04,
+            (byte) 0x03,
+            (byte) 0x02,
+            (byte) 0x01,
+            // Eddystone ID instance
+            (byte) 0x16,
+            (byte) 0x15,
+            (byte) 0x14,
+            (byte) 0x13,
+            (byte) 0x12,
+            (byte) 0x11,
+            // RFU
+            (byte) 0x00,
+            (byte) 0x00
+    };
 }
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java
index 637cd03..f6e77e6 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Bytes.java
@@ -17,6 +17,7 @@
 package com.android.server.nearby.common.bluetooth.fastpair;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -51,7 +52,8 @@
             return this.mByteOrder.equals(byteOrder) ? getBytes() : reverse(getBytes());
         }
 
-        private static byte[] reverse(byte[] bytes) {
+        @VisibleForTesting
+        static byte[] reverse(byte[] bytes) {
             byte[] reversedBytes = new byte[bytes.length];
             for (int i = 0; i < bytes.length; i++) {
                 reversedBytes[i] = bytes[bytes.length - i - 1];
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
index 0b50dfd..7a0548b 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
@@ -133,14 +133,11 @@
             Event that = (Event) o;
             return this.mEventCode == that.getEventCode()
                     && this.mTimestamp == that.getTimestamp()
-                    && (this.mProfile == null
-                        ? that.getProfile() == null : this.mProfile.equals(that.getProfile()))
                     && (this.mBluetoothDevice == null
-                        ? that.getBluetoothDevice() == null :
-                            this.mBluetoothDevice.equals(that.getBluetoothDevice()))
-                    && (this.mException == null
-                        ?  that.getException() == null :
-                            this.mException.equals(that.getException()));
+                    ? that.getBluetoothDevice() == null :
+                    this.mBluetoothDevice.equals(that.getBluetoothDevice()))
+                    && (this.mProfile == null
+                    ? that.getProfile() == null : this.mProfile.equals(that.getProfile()));
         }
         return false;
     }
@@ -150,7 +147,6 @@
         return Objects.hash(mEventCode, mTimestamp, mProfile, mBluetoothDevice, mException);
     }
 
-
     /**
      * Builder
      */
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
index 789ef59..60afa31 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnection.java
@@ -1993,7 +1993,8 @@
         return getLeState(bluetoothAdapter);
     }
 
-    private static int getLeState(android.bluetooth.BluetoothAdapter adapter) {
+    @VisibleForTesting
+    static int getLeState(android.bluetooth.BluetoothAdapter adapter) {
         try {
             return (Integer) Reflect.on(adapter).withMethod("getLeState").get();
         } catch (ReflectionException e) {
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java
index bb7b71b..eb5bad5 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Preferences.java
@@ -1042,104 +1042,6 @@
      */
     public static Builder builder() {
         return new Preferences.Builder()
-                .setGattOperationTimeoutSeconds(3)
-                .setGattConnectionTimeoutSeconds(15)
-                .setBluetoothToggleTimeoutSeconds(10)
-                .setBluetoothToggleSleepSeconds(2)
-                .setClassicDiscoveryTimeoutSeconds(10)
-                .setNumDiscoverAttempts(3)
-                .setDiscoveryRetrySleepSeconds(1)
-                .setIgnoreDiscoveryError(false)
-                .setSdpTimeoutSeconds(10)
-                .setNumSdpAttempts(3)
-                .setNumCreateBondAttempts(3)
-                .setNumConnectAttempts(1)
-                .setNumWriteAccountKeyAttempts(3)
-                .setToggleBluetoothOnFailure(false)
-                .setBluetoothStateUsesPolling(true)
-                .setBluetoothStatePollingMillis(1000)
-                .setNumAttempts(2)
-                .setEnableBrEdrHandover(false)
-                .setBrHandoverDataCharacteristicId(get16BitUuid(
-                        Constants.TransportDiscoveryService.BrHandoverDataCharacteristic.ID))
-                .setBluetoothSigDataCharacteristicId(get16BitUuid(
-                        Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic.ID))
-                .setFirmwareVersionCharacteristicId(get16BitUuid(FirmwareVersionCharacteristic.ID))
-                .setBrTransportBlockDataDescriptorId(
-                        get16BitUuid(
-                                Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic
-                                        .BrTransportBlockDataDescriptor.ID))
-                .setWaitForUuidsAfterBonding(true)
-                .setReceiveUuidsAndBondedEventBeforeClose(true)
-                .setRemoveBondTimeoutSeconds(5)
-                .setRemoveBondSleepMillis(1000)
-                .setCreateBondTimeoutSeconds(15)
-                .setHidCreateBondTimeoutSeconds(40)
-                .setProxyTimeoutSeconds(2)
-                .setRejectPhonebookAccess(false)
-                .setRejectMessageAccess(false)
-                .setRejectSimAccess(false)
-                .setAcceptPasskey(true)
-                .setSupportedProfileUuids(Constants.getSupportedProfiles())
-                .setWriteAccountKeySleepMillis(2000)
-                .setProviderInitiatesBondingIfSupported(false)
-                .setAttemptDirectConnectionWhenPreviouslyBonded(false)
-                .setAutomaticallyReconnectGattWhenNeeded(false)
-                .setSkipDisconnectingGattBeforeWritingAccountKey(false)
-                .setSkipConnectingProfiles(false)
-                .setIgnoreUuidTimeoutAfterBonded(false)
-                .setSpecifyCreateBondTransportType(false)
-                .setCreateBondTransportType(0 /*BluetoothDevice.TRANSPORT_AUTO*/)
-                .setIncreaseIntentFilterPriority(true)
-                .setEvaluatePerformance(false)
-                .setKeepSameAccountKeyWrite(true)
-                .setEnableNamingCharacteristic(false)
-                .setEnableFirmwareVersionCharacteristic(false)
-                .setIsRetroactivePairing(false)
-                .setNumSdpAttemptsAfterBonded(1)
-                .setSupportHidDevice(false)
-                .setEnablePairingWhileDirectlyConnecting(true)
-                .setAcceptConsentForFastPairOne(true)
-                .setGattConnectRetryTimeoutMillis(0)
-                .setEnable128BitCustomGattCharacteristicsId(true)
-                .setEnableSendExceptionStepToValidator(true)
-                .setEnableAdditionalDataTypeWhenActionOverBle(true)
-                .setCheckBondStateWhenSkipConnectingProfiles(true)
-                .setHandlePasskeyConfirmationByUi(false)
-                .setMoreEventLogForQuality(true)
-                .setRetryGattConnectionAndSecretHandshake(true)
-                .setGattConnectShortTimeoutMs(7000)
-                .setGattConnectLongTimeoutMs(15000)
-                .setGattConnectShortTimeoutRetryMaxSpentTimeMs(10000)
-                .setAddressRotateRetryMaxSpentTimeMs(15000)
-                .setPairingRetryDelayMs(100)
-                .setSecretHandshakeShortTimeoutMs(3000)
-                .setSecretHandshakeLongTimeoutMs(10000)
-                .setSecretHandshakeShortTimeoutRetryMaxSpentTimeMs(5000)
-                .setSecretHandshakeLongTimeoutRetryMaxSpentTimeMs(7000)
-                .setSecretHandshakeRetryAttempts(3)
-                .setSecretHandshakeRetryGattConnectionMaxSpentTimeMs(15000)
-                .setSignalLostRetryMaxSpentTimeMs(15000)
-                .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of())
-                .setRetrySecretHandshakeTimeout(false)
-                .setLogUserManualRetry(true)
-                .setPairFailureCounts(0)
-                .setEnablePairFlowShowUiWithoutProfileConnection(true)
-                .setPairFailureCounts(0)
-                .setLogPairWithCachedModelId(true)
-                .setDirectConnectProfileIfModelIdInCache(false)
-                .setCachedDeviceAddress("")
-                .setPossibleCachedDeviceAddress("")
-                .setSameModelIdPairedDeviceCount(0)
-                .setIsDeviceFinishCheckAddressFromCache(true);
-    }
-
-    /**
-     * Constructs a builder from GmsLog.
-     */
-    // TODO(b/206668142): remove this builder once api is ready.
-    public static Builder builderFromGmsLog() {
-        return new Preferences.Builder()
                 .setGattOperationTimeoutSeconds(10)
                 .setGattConnectionTimeoutSeconds(15)
                 .setBluetoothToggleTimeoutSeconds(10)
@@ -1158,10 +1060,15 @@
                 .setBluetoothStatePollingMillis(1000)
                 .setNumAttempts(2)
                 .setEnableBrEdrHandover(false)
-                .setBrHandoverDataCharacteristicId((short) 11265)
-                .setBluetoothSigDataCharacteristicId((short) 11266)
-                .setFirmwareVersionCharacteristicId((short) 10790)
-                .setBrTransportBlockDataDescriptorId((short) 11267)
+                .setBrHandoverDataCharacteristicId(get16BitUuid(
+                        Constants.TransportDiscoveryService.BrHandoverDataCharacteristic.ID))
+                .setBluetoothSigDataCharacteristicId(get16BitUuid(
+                        Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic.ID))
+                .setFirmwareVersionCharacteristicId(get16BitUuid(FirmwareVersionCharacteristic.ID))
+                .setBrTransportBlockDataDescriptorId(
+                        get16BitUuid(
+                                Constants.TransportDiscoveryService.BluetoothSigDataCharacteristic
+                                        .BrTransportBlockDataDescriptor.ID))
                 .setWaitForUuidsAfterBonding(true)
                 .setReceiveUuidsAndBondedEventBeforeClose(true)
                 .setRemoveBondTimeoutSeconds(5)
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java
index 5b45f61..b2002c5 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDevice.java
@@ -187,12 +187,6 @@
         return mWrappedBluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);
     }
 
-    /** See {@link android.bluetooth.BluetoothDevice#setPin(byte[])}. */
-    @TargetApi(19)
-    public boolean setPairingConfirmation(byte[] pin) {
-        return mWrappedBluetoothDevice.setPin(pin);
-    }
-
     /** See {@link android.bluetooth.BluetoothDevice#setPairingConfirmation(boolean)}. */
     public boolean setPairingConfirmation(boolean confirm) {
         return mWrappedBluetoothDevice.setPairingConfirmation(confirm);
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java
index 3f6f361..d4873fd 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServer.java
@@ -51,6 +51,11 @@
         return new BluetoothGattServer(instance);
     }
 
+    /** Unwraps a Bluetooth Gatt server. */
+    public android.bluetooth.BluetoothGattServer unwrap() {
+        return mWrappedInstance;
+    }
+
     /**
      * See {@link android.bluetooth.BluetoothGattServer#connect(
      * android.bluetooth.BluetoothDevice, boolean)}
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java
index 6fe4432..b2c61ab 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -71,4 +71,10 @@
         }
         return new BluetoothLeAdvertiser(bluetoothLeAdvertiser);
     }
+
+    /** Unwraps a Bluetooth LE advertiser. */
+    @Nullable
+    public android.bluetooth.le.BluetoothLeAdvertiser unwrap() {
+        return mWrappedInstance;
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java
index 8a13abe..9b3447e 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScanner.java
@@ -77,6 +77,12 @@
         mWrappedBluetoothLeScanner.stopScan(callbackIntent);
     }
 
+    /** Unwraps a Bluetooth LE scanner. */
+    @Nullable
+    public android.bluetooth.le.BluetoothLeScanner unwrap() {
+        return mWrappedBluetoothLeScanner;
+    }
+
     /** Wraps a Bluetooth LE scanner. */
     @Nullable
     public static BluetoothLeScanner wrap(
diff --git a/nearby/service/java/com/android/server/nearby/common/locator/Locator.java b/nearby/service/java/com/android/server/nearby/common/locator/Locator.java
index f8b43a6..2003335 100644
--- a/nearby/service/java/com/android/server/nearby/common/locator/Locator.java
+++ b/nearby/service/java/com/android/server/nearby/common/locator/Locator.java
@@ -110,7 +110,8 @@
         throw new IllegalStateException(errorMessage);
     }
 
-    private String getUnboundErrorMessage(Class<?> type) {
+    @VisibleForTesting
+    String getUnboundErrorMessage(Class<?> type) {
         StringBuilder sb = new StringBuilder();
         sb.append("Unbound type: ").append(type.getName()).append("\n").append(
                 "Searched locators:\n");
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
index 2ecce47..d459329 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairAdvHandler.java
@@ -31,10 +31,7 @@
 import com.android.server.nearby.common.ble.decode.FastPairDecoder;
 import com.android.server.nearby.common.ble.util.RangingUtils;
 import com.android.server.nearby.common.bloomfilter.BloomFilter;
-import com.android.server.nearby.common.bloomfilter.FastPairBloomFilterHasher;
 import com.android.server.nearby.common.locator.Locator;
-import com.android.server.nearby.fastpair.cache.DiscoveryItem;
-import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
 import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
 import com.android.server.nearby.provider.FastPairDataProvider;
 import com.android.server.nearby.util.DataUtils;
@@ -120,7 +117,7 @@
                 Log.e(TAG, "OEM does not construct fast pair data proxy correctly");
             }
         } else {
-            // Start to process bloom filter
+            // Start to process bloom filter. Yet to finish.
             try {
                 List<Account> accountList = mPairDataProvider.loadFastPairEligibleAccounts();
                 byte[] bloomFilterByteArray = FastPairDecoder
@@ -130,73 +127,9 @@
                 if (bloomFilterByteArray == null || bloomFilterByteArray.length == 0) {
                     return;
                 }
-                for (Account account : accountList) {
-                    List<Data.FastPairDeviceWithAccountKey> listDevices =
-                            mPairDataProvider.loadFastPairDeviceWithAccountKey(account);
-                    Data.FastPairDeviceWithAccountKey recognizedDevice =
-                            findRecognizedDevice(listDevices,
-                                    new BloomFilter(bloomFilterByteArray,
-                                            new FastPairBloomFilterHasher()), bloomFilterSalt);
-
-                    if (recognizedDevice != null) {
-                        Log.d(TAG, "find matched device show notification to remind"
-                                + " user to pair");
-                        // Check the distance of the device if the distance is larger than the
-                        // threshold
-                        // do not show half sheet.
-                        if (!isNearby(fastPairDevice.getRssi(),
-                                recognizedDevice.getDiscoveryItem().getTxPower() == 0
-                                        ? fastPairDevice.getTxPower()
-                                        : recognizedDevice.getDiscoveryItem().getTxPower())) {
-                            return;
-                        }
-                        // Check if the device is already paired
-                        List<Cache.StoredFastPairItem> storedFastPairItemList =
-                                Locator.get(mContext, FastPairCacheManager.class)
-                                        .getAllSavedStoredFastPairItem();
-                        Cache.StoredFastPairItem recognizedStoredFastPairItem =
-                                findRecognizedDeviceFromCachedItem(storedFastPairItemList,
-                                        new BloomFilter(bloomFilterByteArray,
-                                                new FastPairBloomFilterHasher()), bloomFilterSalt);
-                        if (recognizedStoredFastPairItem != null) {
-                            // The bloomfilter is recognized in the cache so the device is paired
-                            // before
-                            Log.d(TAG, "bloom filter is recognized in the cache");
-                            continue;
-                        } else {
-                            Log.d(TAG, "bloom filter is recognized not paired before should"
-                                    + "show subsequent pairing notification");
-                            if (mIsFirst) {
-                                mIsFirst = false;
-                                // Get full info from api the initial request will only return
-                                // part of the info due to size limit.
-                                List<Data.FastPairDeviceWithAccountKey> resList =
-                                        mPairDataProvider.loadFastPairDeviceWithAccountKey(account,
-                                                List.of(recognizedDevice.getAccountKey()
-                                                        .toByteArray()));
-                                if (resList != null && resList.size() > 0) {
-                                    //Saved device from footprint does not have ble address so
-                                    // fill ble address with current scan result.
-                                    Cache.StoredDiscoveryItem storedDiscoveryItem =
-                                            resList.get(0).getDiscoveryItem().toBuilder()
-                                                    .setMacAddress(
-                                                            fastPairDevice.getBluetoothAddress())
-                                                    .build();
-                                    Locator.get(mContext, FastPairController.class).pair(
-                                            new DiscoveryItem(mContext, storedDiscoveryItem),
-                                            resList.get(0).getAccountKey().toByteArray(),
-                                            /** companionApp=*/null);
-                                }
-                            }
-                        }
-
-                        return;
-                    }
-                }
             } catch (IllegalStateException e) {
                 Log.e(TAG, "OEM does not construct fast pair data proxy correctly");
             }
-
         }
     }
 
@@ -249,5 +182,4 @@
     boolean isNearby(int rssi, int txPower) {
         return RangingUtils.distanceFromRssiAndTxPower(rssi, txPower) < NEARBY_DISTANCE_THRESHOLD;
     }
-
 }
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
index f368080..e3de4e2 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -242,7 +242,7 @@
 
             String modelId = item.getTriggerId();
             Preferences.Builder prefsBuilder =
-                    Preferences.builderFromGmsLog()
+                    Preferences.builder()
                             .setEnableBrEdrHandover(false)
                             .setIgnoreDiscoveryError(true);
             pairingProgressHandlerBase.onSetupPreferencesBuilder(prefsBuilder);
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java
index 6065f99..5ce4488 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/cache/DiscoveryItem.java
@@ -28,6 +28,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.common.ble.util.RangingUtils;
 import com.android.server.nearby.common.fastpair.IconUtils;
 import com.android.server.nearby.common.locator.Locator;
@@ -106,15 +107,6 @@
     }
 
     /**
-     * Sets the store discovery item mac address.
-     */
-    public void setMacAddress(String address) {
-        mStoredDiscoveryItem = mStoredDiscoveryItem.toBuilder().setMacAddress(address).build();
-
-        mFastPairCacheManager.saveDiscoveryItem(this);
-    }
-
-    /**
      * Checks if the item is expired. Expired items are those over getItemExpirationMillis() eg. 2
      * minutes
      */
@@ -295,7 +287,8 @@
      * Returns the app name of discovery item.
      */
     @Nullable
-    private String getAppName() {
+    @VisibleForTesting
+    protected String getAppName() {
         return mStoredDiscoveryItem.getAppName();
     }
 
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
index b840091..c6134f5 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/cache/FastPairCacheManager.java
@@ -64,16 +64,6 @@
     }
 
     /**
-     * Checks if the entry can be auto deleted from the cache
-     */
-    public boolean isDeletable(Cache.ServerResponseDbItem entry) {
-        if (!entry.getExpirable()) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
      * Save discovery item into database. Discovery item is item that discovered through Ble before
      * pairing success.
      */
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
index ccd7e5e..5fb05d5 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection;
 import com.android.server.nearby.common.bluetooth.fastpair.Preferences;
 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
@@ -184,7 +185,8 @@
                 + maskBluetoothAddress(address));
     }
 
-    private static void optInFootprintsForInitialPairing(
+    @VisibleForTesting
+    static void optInFootprintsForInitialPairing(
             FootprintsDeviceManager footprints,
             DiscoveryItem item,
             byte[] accountKey,
diff --git a/nearby/service/java/com/android/server/nearby/injector/Injector.java b/nearby/service/java/com/android/server/nearby/injector/Injector.java
index 57784a9..3152ee6 100644
--- a/nearby/service/java/com/android/server/nearby/injector/Injector.java
+++ b/nearby/service/java/com/android/server/nearby/injector/Injector.java
@@ -18,6 +18,7 @@
 
 import android.app.AppOpsManager;
 import android.bluetooth.BluetoothAdapter;
+import android.hardware.location.ContextHubManager;
 
 /**
  * Nearby dependency injector. To be used for accessing various Nearby class instances and as a
@@ -29,7 +30,7 @@
     BluetoothAdapter getBluetoothAdapter();
 
     /** Get the ContextHubManagerAdapter for ChreDiscoveryProvider to scan. */
-    ContextHubManagerAdapter getContextHubManagerAdapter();
+    ContextHubManager getContextHubManager();
 
     /** Get the AppOpsManager to control access. */
     AppOpsManager getAppOpsManager();
diff --git a/nearby/service/java/com/android/server/nearby/presence/Advertisement.java b/nearby/service/java/com/android/server/nearby/presence/Advertisement.java
new file mode 100644
index 0000000..d42f6c7
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/Advertisement.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import android.annotation.Nullable;
+import android.nearby.BroadcastRequest;
+import android.nearby.PresenceCredential;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A Nearby Presence advertisement to be advertised. */
+public abstract class Advertisement {
+
+    @BroadcastRequest.BroadcastVersion
+    int mVersion = BroadcastRequest.PRESENCE_VERSION_UNKNOWN;
+    int mLength;
+    @PresenceCredential.IdentityType int mIdentityType;
+    byte[] mIdentity;
+    byte[] mSalt;
+    List<Integer> mActions;
+
+    /** Serialize an {@link Advertisement} object into bytes. */
+    @Nullable
+    public byte[] toBytes() {
+        return new byte[0];
+    }
+
+    /** Returns the length of the advertisement. */
+    public int getLength() {
+        return mLength;
+    }
+
+    /** Returns the version in the advertisement. */
+    @BroadcastRequest.BroadcastVersion
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /** Returns the identity type in the advertisement. */
+    @PresenceCredential.IdentityType
+    public int getIdentityType() {
+        return mIdentityType;
+    }
+
+    /** Returns the identity bytes in the advertisement. */
+    public byte[] getIdentity() {
+        return mIdentity.clone();
+    }
+
+    /** Returns the salt of the advertisement. */
+    public byte[] getSalt() {
+        return mSalt.clone();
+    }
+
+    /** Returns the actions in the advertisement. */
+    public List<Integer> getActions() {
+        return new ArrayList<>(mActions);
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/DataElementHeader.java b/nearby/service/java/com/android/server/nearby/presence/DataElementHeader.java
new file mode 100644
index 0000000..ae4a728
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/DataElementHeader.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import android.annotation.Nullable;
+import android.nearby.BroadcastRequest;
+import android.nearby.DataElement;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a data element header in Nearby Presence.
+ * Each header has 3 parts: tag, length and style.
+ * Tag: 1 bit (MSB at each byte). 1 for extending, which means there will be more bytes after
+ * the current one for the header.
+ * Length: The total length of a Data Element field. Length is up to 127 and is limited within
+ * the entire first byte in the header. (7 bits, MSB is the tag).
+ * Type: Represents {@link DataElement.DataType}. There is no limit for the type number.
+ *
+ * @hide
+ */
+public class DataElementHeader {
+    // Each Data reserved MSB for tag.
+    static final int TAG_BITMASK = 0b10000000;
+    static final int TAG_OFFSET = 7;
+
+    // If the header is only 1 byte, it has the format: 0b0LLLTTTT. (L for length, T for type.)
+    static final int SINGLE_AVAILABLE_LENGTH_BIT = 3;
+    static final int SINGLE_AVAILABLE_TYPE_BIT = 4;
+    static final int SINGLE_LENGTH_BITMASK = 0b01110000;
+    static final int SINGLE_LENGTH_OFFSET = SINGLE_AVAILABLE_TYPE_BIT;
+    static final int SINGLE_TYPE_BITMASK = 0b00001111;
+
+    // If there are multiple data element headers.
+    // First byte is always the length.
+    static final int MULTIPLE_LENGTH_BYTE = 1;
+    // Each byte reserves MSB for tag.
+    static final int MULTIPLE_BITMASK = 0b01111111;
+
+    @BroadcastRequest.BroadcastVersion
+    private final int mVersion;
+    @DataElement.DataType
+    private final int mDataType;
+    private final int mDataLength;
+
+    DataElementHeader(@BroadcastRequest.BroadcastVersion int version,
+            @DataElement.DataType int dataType, int dataLength) {
+        Preconditions.checkArgument(version == BroadcastRequest.PRESENCE_VERSION_V1,
+                "DataElementHeader is only supported in V1.");
+        Preconditions.checkArgument(dataLength >= 0, "Length should not be negative.");
+        Preconditions.checkArgument(dataLength < (1 << TAG_OFFSET),
+                "Data element should be equal or shorter than 128.");
+
+        this.mVersion = version;
+        this.mDataType = dataType;
+        this.mDataLength = dataLength;
+    }
+
+    /**
+     * The total type of the data element.
+     */
+    @DataElement.DataType
+    public int getDataType() {
+        return mDataType;
+    }
+
+    /**
+     * The total length of a Data Element field.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
+
+    /** Serialize a {@link DataElementHeader} object into bytes. */
+    public byte[] toBytes() {
+        Preconditions.checkState(mVersion == BroadcastRequest.PRESENCE_VERSION_V1,
+                "DataElementHeader is only supported in V1.");
+        // Only 1 byte needed for the header
+        if (mDataType < (1 << SINGLE_AVAILABLE_TYPE_BIT)
+                && mDataLength < (1 << SINGLE_AVAILABLE_LENGTH_BIT)) {
+            return new byte[]{createSingleByteHeader(mDataType, mDataLength)};
+        }
+
+        return createMultipleBytesHeader(mDataType, mDataLength);
+    }
+
+    /** Creates a {@link DataElementHeader} object from bytes. */
+    @Nullable
+    public static DataElementHeader fromBytes(@BroadcastRequest.BroadcastVersion int version,
+            @Nonnull byte[] bytes) {
+        Objects.requireNonNull(bytes, "Data parsed in for DataElement should not be null.");
+
+        if (bytes.length == 0) {
+            return null;
+        }
+
+        if (bytes.length == 1) {
+            if (isExtending(bytes[0])) {
+                throw new IllegalArgumentException("The header is not complete.");
+            }
+            return new DataElementHeader(BroadcastRequest.PRESENCE_VERSION_V1,
+                    getTypeSingleByte(bytes[0]), getLengthSingleByte(bytes[0]));
+        }
+
+        // The first byte should be length and there should be at least 1 more byte following to
+        // represent type.
+        // The last header byte's MSB should be 0.
+        if (!isExtending(bytes[0]) || isExtending(bytes[bytes.length - 1])) {
+            throw new IllegalArgumentException("The header format is wrong.");
+        }
+
+        return new DataElementHeader(version,
+                getTypeMultipleBytes(Arrays.copyOfRange(bytes, 1, bytes.length)),
+                getHeaderValue(bytes[0]));
+    }
+
+    /** Creates a header based on type and length.
+     * This is used when the type is <= 16 and length is <= 7. */
+    static byte createSingleByteHeader(int type, int length) {
+        return (byte) (convertTag(/* extend= */ false)
+                | convertLengthSingleByte(length)
+                | convertTypeSingleByte(type));
+    }
+
+    /** Creates a header based on type and length.
+     * This is used when the type is > 16 or length is > 7. */
+    static byte[] createMultipleBytesHeader(int type, int length) {
+        List<Byte> typeIntList = convertTypeMultipleBytes(type);
+        byte[] res = new byte[typeIntList.size() + MULTIPLE_LENGTH_BYTE];
+        int index = 0;
+        res[index++] = convertLengthMultipleBytes(length);
+
+        for (int typeInt : typeIntList) {
+            res[index++] = (byte) typeInt;
+        }
+        return res;
+    }
+
+    /** Constructs a Data Element header with length indicated in byte format.
+     * The most significant bit is the tag, 2- 4 bits are the length, 5 - 8 bits are the type.
+     */
+    @VisibleForTesting
+    static int convertLengthSingleByte(int length) {
+        Preconditions.checkArgument(length >= 0, "Length should not be negative.");
+        Preconditions.checkArgument(length < (1 << SINGLE_AVAILABLE_LENGTH_BIT),
+                "In single Data Element header, length should be shorter than 8.");
+        return (length << SINGLE_LENGTH_OFFSET) & SINGLE_LENGTH_BITMASK;
+    }
+
+    /** Constructs a Data Element header with type indicated in byte format.
+     * The most significant bit is the tag, 2- 4 bits are the length, 5 - 8 bits are the type.
+     */
+    @VisibleForTesting
+    static int convertTypeSingleByte(int type) {
+        Preconditions.checkArgument(type >= 0, "Type should not be negative.");
+        Preconditions.checkArgument(type < (1 << SINGLE_AVAILABLE_TYPE_BIT),
+                "In single Data Element header, type should be smaller than 16.");
+
+        return type & SINGLE_TYPE_BITMASK;
+    }
+
+    /**
+     * Gets the length of Data Element from the header. (When there is only 1 byte of header)
+     */
+    static int getLengthSingleByte(byte header) {
+        Preconditions.checkArgument(!isExtending(header),
+                "Cannot apply this method for the extending header.");
+        return (header & SINGLE_LENGTH_BITMASK) >> SINGLE_LENGTH_OFFSET;
+    }
+
+    /**
+     * Gets the type of Data Element from the header. (When there is only 1 byte of header)
+     */
+    static int getTypeSingleByte(byte header) {
+        Preconditions.checkArgument(!isExtending(header),
+                "Cannot apply this method for the extending header.");
+        return header & SINGLE_TYPE_BITMASK;
+    }
+
+    /** Creates a DE(data element) header based on length.
+     * This is used when header is more than 1 byte. The first byte is always the length.
+     */
+    static byte convertLengthMultipleBytes(int length) {
+        Preconditions.checkArgument(length < (1 << TAG_OFFSET),
+                "Data element should be equal or shorter than 128.");
+        return (byte) (convertTag(/* extend= */ true) | (length & MULTIPLE_BITMASK));
+    }
+
+    /** Creates a DE(data element) header based on type.
+     * This is used when header is more than 1 byte. The first byte is always the length.
+     */
+    @VisibleForTesting
+    static List<Byte> convertTypeMultipleBytes(int type) {
+        List<Byte> typeBytes = new ArrayList<>();
+        while (type > 0) {
+            byte current = (byte) (type & MULTIPLE_BITMASK);
+            type = type >> TAG_OFFSET;
+            typeBytes.add(current);
+        }
+
+        Collections.reverse(typeBytes);
+        int size = typeBytes.size();
+        // The last byte's MSB should be 0.
+        for (int i = 0; i < size - 1; i++) {
+            typeBytes.set(i, (byte) (convertTag(/* extend= */ true) | typeBytes.get(i)));
+        }
+        return typeBytes;
+    }
+
+    /** Creates a DE(data element) header based on type.
+     * This is used when header is more than 1 byte. The first byte is always the length.
+     * Uses Integer when doing bit operation to avoid error.
+     */
+    @VisibleForTesting
+    static int getTypeMultipleBytes(byte[] typeByteArray) {
+        int type = 0;
+        int size = typeByteArray.length;
+        for (int i = 0; i < size; i++) {
+            type = (type << TAG_OFFSET) | getHeaderValue(typeByteArray[i]);
+        }
+        return type;
+    }
+
+    /** Gets the integer value of the 7 bits in the header. (The MSB is tag) */
+    @VisibleForTesting
+    static int getHeaderValue(byte header) {
+        return (header & MULTIPLE_BITMASK);
+    }
+
+    /** Sets the MSB of the header byte. If this is the last byte of headers, MSB is 0.
+     * If there are at least header following, the MSB is 1.
+     */
+    @VisibleForTesting
+    static byte convertTag(boolean extend) {
+        return (byte) (extend ? 0b10000000 : 0b00000000);
+    }
+
+    /** Returns {@code true} if there are at least 1 byte of header after the current one. */
+    @VisibleForTesting
+    static boolean isExtending(byte header) {
+        return (header & TAG_BITMASK) != 0;
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisement.java b/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisement.java
new file mode 100644
index 0000000..34a7514
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisement.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.annotation.Nullable;
+import android.nearby.BroadcastRequest;
+import android.nearby.DataElement;
+import android.nearby.PresenceBroadcastRequest;
+import android.nearby.PresenceCredential;
+import android.nearby.PublicCredential;
+import android.util.Log;
+
+import com.android.server.nearby.util.encryption.Cryptor;
+import com.android.server.nearby.util.encryption.CryptorImpFake;
+import com.android.server.nearby.util.encryption.CryptorImpIdentityV1;
+import com.android.server.nearby.util.encryption.CryptorImpV1;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A Nearby Presence advertisement to be advertised on BT5.0 devices.
+ *
+ * <p>Serializable between Java object and bytes formats. Java object is used at the upper scanning
+ * and advertising interface as an abstraction of the actual bytes. Bytes format is used at the
+ * underlying BLE and mDNS stacks, which do necessary slicing and merging based on advertising
+ * capacities.
+ *
+ * The extended advertisement is defined in the format below:
+ * Header (1 byte) | salt (1+2 bytes) | Identity + filter (2+16 bytes)
+ * | repeated DE fields (various bytes)
+ * The header contains:
+ * version (3 bits) | 5 bit reserved for future use (RFU)
+ */
+public class ExtendedAdvertisement extends Advertisement{
+
+    public static final int SALT_DATA_LENGTH = 2;
+
+    static final int HEADER_LENGTH = 1;
+
+    static final int IDENTITY_DATA_LENGTH = 16;
+
+    private final List<DataElement> mDataElements;
+
+    private final byte[] mAuthenticityKey;
+
+    // All Data Elements including salt and identity.
+    // Each list item (byte array) is a Data Element (with its header).
+    private final List<byte[]> mCompleteDataElementsBytes;
+    // Signature generated from data elements.
+    private final byte[] mHmacTag;
+
+    /**
+     * Creates an {@link ExtendedAdvertisement} from a Presence Broadcast Request.
+     * @return {@link ExtendedAdvertisement} object. {@code null} when the request is illegal.
+     */
+    @Nullable
+    public static ExtendedAdvertisement createFromRequest(PresenceBroadcastRequest request) {
+        if (request.getVersion() != BroadcastRequest.PRESENCE_VERSION_V1) {
+            Log.v(TAG, "ExtendedAdvertisement only supports V1 now.");
+            return null;
+        }
+
+        byte[] salt = request.getSalt();
+        if (salt.length != SALT_DATA_LENGTH) {
+            Log.v(TAG, "Salt does not match correct length");
+            return null;
+        }
+
+        byte[] identity = request.getCredential().getMetadataEncryptionKey();
+        byte[] authenticityKey = request.getCredential().getAuthenticityKey();
+        if (identity.length != IDENTITY_DATA_LENGTH) {
+            Log.v(TAG, "Identity does not match correct length");
+            return null;
+        }
+
+        List<Integer> actions = request.getActions();
+        if (actions.isEmpty()) {
+            Log.v(TAG, "ExtendedAdvertisement must contain at least one action");
+            return null;
+        }
+
+        List<DataElement> dataElements = request.getExtendedProperties();
+        return new ExtendedAdvertisement(
+                request.getCredential().getIdentityType(),
+                identity,
+                salt,
+                authenticityKey,
+                actions,
+                dataElements);
+    }
+
+    /** Serialize an {@link ExtendedAdvertisement} object into bytes with {@link DataElement}s */
+    @Nullable
+    public byte[] toBytes() {
+        ByteBuffer buffer = ByteBuffer.allocate(getLength());
+
+        // Header
+        buffer.put(ExtendedAdvertisementUtils.constructHeader(getVersion()));
+
+        // Salt
+        buffer.put(mCompleteDataElementsBytes.get(0));
+
+        // Identity
+        buffer.put(mCompleteDataElementsBytes.get(1));
+
+        List<Byte> rawDataBytes = new ArrayList<>();
+        // Data Elements (Already includes salt and identity)
+        for (int i = 2; i < mCompleteDataElementsBytes.size(); i++) {
+            byte[] dataElementBytes = mCompleteDataElementsBytes.get(i);
+            for (Byte b : dataElementBytes) {
+                rawDataBytes.add(b);
+            }
+        }
+
+        byte[] dataElements = new byte[rawDataBytes.size()];
+        for (int i = 0; i < rawDataBytes.size(); i++) {
+            dataElements[i] = rawDataBytes.get(i);
+        }
+
+        buffer.put(
+                getCryptor(/* encrypt= */ true).encrypt(dataElements, getSalt(), mAuthenticityKey));
+
+        buffer.put(mHmacTag);
+
+        return buffer.array();
+    }
+
+    /** Deserialize from bytes into an {@link ExtendedAdvertisement} object.
+     * {@code null} when there is something when parsing.
+     */
+    @Nullable
+    public static ExtendedAdvertisement fromBytes(byte[] bytes, PublicCredential publicCredential) {
+        @BroadcastRequest.BroadcastVersion
+        int version = ExtendedAdvertisementUtils.getVersion(bytes);
+        if (version != PresenceBroadcastRequest.PRESENCE_VERSION_V1) {
+            Log.v(TAG, "ExtendedAdvertisement is used in V1 only and version is " + version);
+            return null;
+        }
+
+        byte[] authenticityKey = publicCredential.getAuthenticityKey();
+
+        int index = HEADER_LENGTH;
+        // Salt
+        byte[] saltHeaderArray = ExtendedAdvertisementUtils.getDataElementHeader(bytes, index);
+        DataElementHeader saltHeader = DataElementHeader.fromBytes(version, saltHeaderArray);
+        if (saltHeader == null || saltHeader.getDataType() != DataElement.DataType.SALT) {
+            Log.v(TAG, "First data element has to be salt.");
+            return null;
+        }
+        index += saltHeaderArray.length;
+        byte[] salt = new byte[saltHeader.getDataLength()];
+        for (int i = 0; i < saltHeader.getDataLength(); i++) {
+            salt[i] = bytes[index++];
+        }
+
+        // Identity
+        byte[] identityHeaderArray = ExtendedAdvertisementUtils.getDataElementHeader(bytes, index);
+        DataElementHeader identityHeader =
+                DataElementHeader.fromBytes(version, identityHeaderArray);
+        if (identityHeader == null) {
+            Log.v(TAG, "The second element has to be identity.");
+            return null;
+        }
+        index += identityHeaderArray.length;
+        @PresenceCredential.IdentityType int identityType =
+                toPresenceCredentialIdentityType(identityHeader.getDataType());
+        if (identityType == PresenceCredential.IDENTITY_TYPE_UNKNOWN) {
+            Log.v(TAG, "The identity type is unknown.");
+            return null;
+        }
+        byte[] encryptedIdentity = new byte[identityHeader.getDataLength()];
+        for (int i = 0; i < identityHeader.getDataLength(); i++) {
+            encryptedIdentity[i] = bytes[index++];
+        }
+        byte[] identity =
+                CryptorImpIdentityV1
+                        .getInstance().decrypt(encryptedIdentity, salt, authenticityKey);
+
+        Cryptor cryptor = getCryptor(/* encrypt= */ true);
+        byte[] encryptedDataElements =
+                new byte[bytes.length - index - cryptor.getSignatureLength()];
+        // Decrypt other data elements
+        System.arraycopy(bytes, index, encryptedDataElements, 0, encryptedDataElements.length);
+        byte[] decryptedDataElements =
+                cryptor.decrypt(encryptedDataElements, salt, authenticityKey);
+        if (decryptedDataElements == null) {
+            return null;
+        }
+
+        // Verify the computed HMAC tag is equal to HMAC tag in advertisement
+        if (cryptor.getSignatureLength() > 0) {
+            byte[] expectedHmacTag = new byte[cryptor.getSignatureLength()];
+            System.arraycopy(
+                    bytes, bytes.length - cryptor.getSignatureLength(),
+                    expectedHmacTag, 0, cryptor.getSignatureLength());
+            if (!cryptor.verify(decryptedDataElements, authenticityKey, expectedHmacTag)) {
+                Log.e(TAG, "HMAC tags not match.");
+                return null;
+            }
+        }
+
+        int dataElementArrayIndex = 0;
+        // Other Data Elements
+        List<Integer> actions = new ArrayList<>();
+        List<DataElement> dataElements = new ArrayList<>();
+        while (dataElementArrayIndex < decryptedDataElements.length) {
+            byte[] deHeaderArray = ExtendedAdvertisementUtils
+                    .getDataElementHeader(decryptedDataElements, dataElementArrayIndex);
+            DataElementHeader deHeader = DataElementHeader.fromBytes(version, deHeaderArray);
+            dataElementArrayIndex += deHeaderArray.length;
+
+            @DataElement.DataType int type = Objects.requireNonNull(deHeader).getDataType();
+            if (type == DataElement.DataType.ACTION) {
+                if (deHeader.getDataLength() != 1) {
+                    Log.v(TAG, "Action id should only 1 byte.");
+                    return null;
+                }
+                actions.add((int) decryptedDataElements[dataElementArrayIndex++]);
+            } else {
+                if (isSaltOrIdentity(type)) {
+                    Log.v(TAG, "Type " + type + " is duplicated. There should be only one salt"
+                            + " and one identity in the advertisement.");
+                    return null;
+                }
+                byte[] deData = new byte[deHeader.getDataLength()];
+                for (int i = 0; i < deHeader.getDataLength(); i++) {
+                    deData[i] = decryptedDataElements[dataElementArrayIndex++];
+                }
+                dataElements.add(new DataElement(type, deData));
+            }
+        }
+
+        return new ExtendedAdvertisement(identityType, identity, salt, authenticityKey, actions,
+                dataElements);
+    }
+
+    /** Returns the {@link DataElement}s in the advertisement. */
+    public List<DataElement> getDataElements() {
+        return new ArrayList<>(mDataElements);
+    }
+
+    /** Returns the {@link DataElement}s in the advertisement according to the key. */
+    public List<DataElement> getDataElements(@DataElement.DataType int key) {
+        List<DataElement> res = new ArrayList<>();
+        for (DataElement dataElement : mDataElements) {
+            if (key == dataElement.getKey()) {
+                res.add(dataElement);
+            }
+        }
+        return res;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                "ExtendedAdvertisement:"
+                        + "<VERSION: %s, length: %s, dataElementCount: %s, identityType: %s,"
+                        + " identity: %s, salt: %s, actions: %s>",
+                getVersion(),
+                getLength(),
+                getDataElements().size(),
+                getIdentityType(),
+                Arrays.toString(getIdentity()),
+                Arrays.toString(getSalt()),
+                getActions());
+    }
+
+    ExtendedAdvertisement(
+            @PresenceCredential.IdentityType int identityType,
+            byte[] identity,
+            byte[] salt,
+            byte[] authenticityKey,
+            List<Integer> actions,
+            List<DataElement> dataElements) {
+        this.mVersion = BroadcastRequest.PRESENCE_VERSION_V1;
+        this.mIdentityType = identityType;
+        this.mIdentity = identity;
+        this.mSalt = salt;
+        this.mAuthenticityKey = authenticityKey;
+        this.mActions = actions;
+        this.mDataElements = dataElements;
+        this.mCompleteDataElementsBytes = new ArrayList<>();
+
+        int length = HEADER_LENGTH; // header
+
+        // Salt
+        DataElement saltElement = new DataElement(DataElement.DataType.SALT, salt);
+        byte[] saltByteArray = ExtendedAdvertisementUtils.convertDataElementToBytes(saltElement);
+        mCompleteDataElementsBytes.add(saltByteArray);
+        length += saltByteArray.length;
+
+        // Identity
+        byte[] encryptedIdentity =
+                CryptorImpIdentityV1.getInstance().encrypt(identity, salt, authenticityKey);
+        DataElement identityElement = new DataElement(toDataType(identityType), encryptedIdentity);
+        byte[] identityByteArray =
+                ExtendedAdvertisementUtils.convertDataElementToBytes(identityElement);
+        mCompleteDataElementsBytes.add(identityByteArray);
+        length += identityByteArray.length;
+
+        List<Byte> dataElementBytes = new ArrayList<>();
+        // Intents
+        for (int action : mActions) {
+            DataElement actionElement = new DataElement(DataElement.DataType.ACTION,
+                    new byte[] {(byte) action});
+            byte[] intentByteArray =
+                    ExtendedAdvertisementUtils.convertDataElementToBytes(actionElement);
+            mCompleteDataElementsBytes.add(intentByteArray);
+            for (Byte b : intentByteArray) {
+                dataElementBytes.add(b);
+            }
+        }
+
+        // Data Elements (Extended properties)
+        for (DataElement dataElement : mDataElements) {
+            byte[] deByteArray = ExtendedAdvertisementUtils.convertDataElementToBytes(dataElement);
+            mCompleteDataElementsBytes.add(deByteArray);
+            for (Byte b : deByteArray) {
+                dataElementBytes.add(b);
+            }
+        }
+
+        byte[] data = new byte[dataElementBytes.size()];
+        for (int i = 0; i < dataElementBytes.size(); i++) {
+            data[i] = dataElementBytes.get(i);
+        }
+        Cryptor cryptor = getCryptor(/* encrypt= */ true);
+        byte[] encryptedDeBytes = cryptor.encrypt(data, salt, authenticityKey);
+
+        length += encryptedDeBytes.length;
+
+        // Signature
+        byte[] hmacTag = Objects.requireNonNull(cryptor.sign(data, authenticityKey));
+        mHmacTag = hmacTag;
+        length += hmacTag.length;
+
+        this.mLength = length;
+    }
+
+    @PresenceCredential.IdentityType
+    private static int toPresenceCredentialIdentityType(@DataElement.DataType int type) {
+        switch (type) {
+            case DataElement.DataType.PRIVATE_IDENTITY:
+                return PresenceCredential.IDENTITY_TYPE_PRIVATE;
+            case DataElement.DataType.PROVISIONED_IDENTITY:
+                return PresenceCredential.IDENTITY_TYPE_PROVISIONED;
+            case DataElement.DataType.TRUSTED_IDENTITY:
+                return PresenceCredential.IDENTITY_TYPE_TRUSTED;
+            case DataElement.DataType.PUBLIC_IDENTITY:
+            default:
+                return PresenceCredential.IDENTITY_TYPE_UNKNOWN;
+        }
+    }
+
+    @DataElement.DataType
+    private static int toDataType(@PresenceCredential.IdentityType int identityType) {
+        switch (identityType) {
+            case PresenceCredential.IDENTITY_TYPE_PRIVATE:
+                return DataElement.DataType.PRIVATE_IDENTITY;
+            case PresenceCredential.IDENTITY_TYPE_PROVISIONED:
+                return DataElement.DataType.PROVISIONED_IDENTITY;
+            case PresenceCredential.IDENTITY_TYPE_TRUSTED:
+                return DataElement.DataType.TRUSTED_IDENTITY;
+            case PresenceCredential.IDENTITY_TYPE_UNKNOWN:
+            default:
+                return DataElement.DataType.PUBLIC_IDENTITY;
+        }
+    }
+
+    /**
+     * Returns {@code true} if the given {@link DataElement.DataType} is salt, or one of the
+     * identities. Identities should be able to convert to {@link PresenceCredential.IdentityType}s.
+     */
+    private static boolean isSaltOrIdentity(@DataElement.DataType int type) {
+        return type == DataElement.DataType.SALT || type == DataElement.DataType.PRIVATE_IDENTITY
+                || type == DataElement.DataType.TRUSTED_IDENTITY
+                || type == DataElement.DataType.PROVISIONED_IDENTITY
+                || type == DataElement.DataType.PUBLIC_IDENTITY;
+    }
+
+    private static Cryptor getCryptor(boolean encrypt) {
+        if (encrypt) {
+            Log.d(TAG, "get V1 Cryptor");
+            return CryptorImpV1.getInstance();
+        }
+        Log.d(TAG, "get fake Cryptor");
+        return CryptorImpFake.getInstance();
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisementUtils.java b/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisementUtils.java
new file mode 100644
index 0000000..06d0f2b
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/ExtendedAdvertisementUtils.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.android.server.nearby.presence.ExtendedAdvertisement.HEADER_LENGTH;
+
+import android.annotation.SuppressLint;
+import android.nearby.BroadcastRequest;
+import android.nearby.DataElement;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides serialization and deserialization util methods for {@link ExtendedAdvertisement}.
+ */
+public final class ExtendedAdvertisementUtils {
+
+    // Advertisement header related static fields.
+    private static final int VERSION_MASK = 0b11100000;
+    private static final int VERSION_MASK_AFTER_SHIT = 0b00000111;
+    private static final int HEADER_INDEX = 0;
+    private static final int HEADER_VERSION_OFFSET = 5;
+
+    /**
+     * Constructs the header of a {@link ExtendedAdvertisement}.
+     * 3 bit version, and 5 bit reserved for future use (RFU).
+     */
+    public static byte constructHeader(@BroadcastRequest.BroadcastVersion int version) {
+        return (byte) ((version << 5) & VERSION_MASK);
+    }
+
+    /** Returns the {@link BroadcastRequest.BroadcastVersion} from the advertisement
+     * in bytes format. */
+    public static int getVersion(byte[] advertisement) {
+        if (advertisement.length < HEADER_LENGTH) {
+            throw new IllegalArgumentException("Advertisement must contain header");
+        }
+        return ((advertisement[HEADER_INDEX] & VERSION_MASK) >> HEADER_VERSION_OFFSET)
+                & VERSION_MASK_AFTER_SHIT;
+    }
+
+    /** Returns the {@link DataElementHeader} from the advertisement in bytes format. */
+    public static byte[] getDataElementHeader(byte[] advertisement, int startIndex) {
+        Preconditions.checkArgument(startIndex < advertisement.length,
+                "Advertisement has no longer data left.");
+        List<Byte> headerBytes = new ArrayList<>();
+        while (startIndex < advertisement.length) {
+            byte current = advertisement[startIndex];
+            headerBytes.add(current);
+            if (!DataElementHeader.isExtending(current)) {
+                int size = headerBytes.size();
+                byte[] res = new byte[size];
+                for (int i = 0; i < size; i++) {
+                    res[i] = headerBytes.get(i);
+                }
+                return res;
+            }
+            startIndex++;
+        }
+        throw new IllegalArgumentException("There is no end of the DataElement header.");
+    }
+
+    /**
+     * Constructs {@link DataElement}, including header(s) and actual data element data.
+     *
+     * Suppresses warning because {@link DataElement} checks isValidType in constructor.
+     */
+    @SuppressLint("WrongConstant")
+    public static byte[] convertDataElementToBytes(DataElement dataElement) {
+        @DataElement.DataType int type = dataElement.getKey();
+        byte[] data = dataElement.getValue();
+        DataElementHeader header = new DataElementHeader(BroadcastRequest.PRESENCE_VERSION_V1,
+                type, data.length);
+        byte[] headerByteArray = header.toBytes();
+
+        byte[] res = new byte[headerByteArray.length + data.length];
+        System.arraycopy(headerByteArray, 0, res, 0, headerByteArray.length);
+        System.arraycopy(data, 0, res, headerByteArray.length, data.length);
+        return res;
+    }
+
+    private ExtendedAdvertisementUtils() {}
+}
diff --git a/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java
index e4df673..ae53ada 100644
--- a/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java
+++ b/nearby/service/java/com/android/server/nearby/presence/FastAdvertisement.java
@@ -24,7 +24,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -42,7 +41,7 @@
 // The header contains:
 // version (3 bits) | provision_mode_flag (1 bit) | identity_type (3 bits) |
 // extended_advertisement_mode (1 bit)
-public class FastAdvertisement {
+public class FastAdvertisement extends Advertisement {
 
     private static final int FAST_ADVERTISEMENT_MAX_LENGTH = 24;
 
@@ -85,7 +84,8 @@
                 (byte) request.getTxPower());
     }
 
-    /** Serialize an {@link FastAdvertisement} object into bytes. */
+    /** Serialize a {@link FastAdvertisement} object into bytes. */
+    @Override
     public byte[] toBytes() {
         ByteBuffer buffer = ByteBuffer.allocate(getLength());
 
@@ -100,18 +100,8 @@
         return buffer.array();
     }
 
-    private final int mLength;
-
     private final int mLtvFieldCount;
 
-    @PresenceCredential.IdentityType private final int mIdentityType;
-
-    private final byte[] mIdentity;
-
-    private final byte[] mSalt;
-
-    private final List<Integer> mActions;
-
     @Nullable
     private final Byte mTxPower;
 
@@ -121,6 +111,7 @@
             byte[] salt,
             List<Integer> actions,
             @Nullable Byte txPower) {
+        this.mVersion = BroadcastRequest.PRESENCE_VERSION_V0;
         this.mIdentityType = identityType;
         this.mIdentity = identity;
         this.mSalt = salt;
@@ -143,44 +134,12 @@
                 "FastAdvertisement exceeds maximum length");
     }
 
-    /** Returns the version in the advertisement. */
-    @BroadcastRequest.BroadcastVersion
-    public int getVersion() {
-        return BroadcastRequest.PRESENCE_VERSION_V0;
-    }
-
-    /** Returns the identity type in the advertisement. */
-    @PresenceCredential.IdentityType
-    public int getIdentityType() {
-        return mIdentityType;
-    }
-
-    /** Returns the identity bytes in the advertisement. */
-    public byte[] getIdentity() {
-        return mIdentity.clone();
-    }
-
-    /** Returns the salt of the advertisement. */
-    public byte[] getSalt() {
-        return mSalt.clone();
-    }
-
-    /** Returns the actions in the advertisement. */
-    public List<Integer> getActions() {
-        return new ArrayList<>(mActions);
-    }
-
     /** Returns the adjusted TX Power in the advertisement. Null if not available. */
     @Nullable
     public Byte getTxPower() {
         return mTxPower;
     }
 
-    /** Returns the length of the advertisement. */
-    public int getLength() {
-        return mLength;
-    }
-
     /** Returns the count of LTV fields in the advertisement. */
     public int getLtvFieldCount() {
         return mLtvFieldCount;
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
index d1c72ae..5a76d96 100644
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceDiscoveryResult.java
@@ -16,31 +16,55 @@
 
 package com.android.server.nearby.presence;
 
-import android.nearby.NearbyDevice;
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.annotation.NonNull;
+import android.nearby.DataElement;
 import android.nearby.NearbyDeviceParcelable;
 import android.nearby.PresenceDevice;
 import android.nearby.PresenceScanFilter;
 import android.nearby.PublicCredential;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /** Represents a Presence discovery result. */
 public class PresenceDiscoveryResult {
 
     /** Creates a {@link PresenceDiscoveryResult} from the scan data. */
     public static PresenceDiscoveryResult fromDevice(NearbyDeviceParcelable device) {
+        PresenceDevice presenceDevice = device.getPresenceDevice();
+        if (presenceDevice != null) {
+            return new PresenceDiscoveryResult.Builder()
+                    .setTxPower(device.getTxPower())
+                    .setRssi(device.getRssi())
+                    .setSalt(presenceDevice.getSalt())
+                    .setPublicCredential(device.getPublicCredential())
+                    .addExtendedProperties(presenceDevice.getExtendedProperties())
+                    .setEncryptedIdentityTag(device.getEncryptionKeyTag())
+                    .build();
+        }
         byte[] salt = device.getSalt();
         if (salt == null) {
             salt = new byte[0];
         }
-        return new PresenceDiscoveryResult.Builder()
-                .setTxPower(device.getTxPower())
+
+        PresenceDiscoveryResult.Builder builder = new PresenceDiscoveryResult.Builder();
+        builder.setTxPower(device.getTxPower())
                 .setRssi(device.getRssi())
                 .setSalt(salt)
                 .addPresenceAction(device.getAction())
-                .setPublicCredential(device.getPublicCredential())
-                .build();
+                .setPublicCredential(device.getPublicCredential());
+        if (device.getPresenceDevice() != null) {
+            builder.addExtendedProperties(device.getPresenceDevice().getExtendedProperties());
+        }
+        return builder.build();
     }
 
     private final int mTxPower;
@@ -48,25 +72,35 @@
     private final byte[] mSalt;
     private final List<Integer> mPresenceActions;
     private final PublicCredential mPublicCredential;
+    private final List<DataElement> mExtendedProperties;
+    private final byte[] mEncryptedIdentityTag;
 
     private PresenceDiscoveryResult(
             int txPower,
             int rssi,
             byte[] salt,
             List<Integer> presenceActions,
-            PublicCredential publicCredential) {
+            PublicCredential publicCredential,
+            List<DataElement> extendedProperties,
+            byte[] encryptedIdentityTag) {
         mTxPower = txPower;
         mRssi = rssi;
         mSalt = salt;
         mPresenceActions = presenceActions;
         mPublicCredential = publicCredential;
+        mExtendedProperties = extendedProperties;
+        mEncryptedIdentityTag = encryptedIdentityTag;
     }
 
     /** Returns whether the discovery result matches the scan filter. */
     public boolean matches(PresenceScanFilter scanFilter) {
+        if (accountKeyMatches(scanFilter.getExtendedProperties())) {
+            return true;
+        }
+
         return pathLossMatches(scanFilter.getMaxPathLoss())
                 && actionMatches(scanFilter.getPresenceActions())
-                && credentialMatches(scanFilter.getCredentials());
+                && identityMatches(scanFilter.getCredentials());
     }
 
     private boolean pathLossMatches(int maxPathLoss) {
@@ -80,21 +114,47 @@
         return filterActions.stream().anyMatch(mPresenceActions::contains);
     }
 
-    private boolean credentialMatches(List<PublicCredential> credentials) {
-        return credentials.contains(mPublicCredential);
+    @VisibleForTesting
+    boolean accountKeyMatches(List<DataElement> extendedProperties) {
+        Set<byte[]> accountKeys = new ArraySet<>();
+        for (DataElement requestedDe : mExtendedProperties) {
+            if (requestedDe.getKey() != DataElement.DataType.ACCOUNT_KEY_DATA) {
+                continue;
+            }
+            accountKeys.add(requestedDe.getValue());
+        }
+        for (DataElement scannedDe : extendedProperties) {
+            if (scannedDe.getKey() != DataElement.DataType.ACCOUNT_KEY_DATA) {
+                continue;
+            }
+            // If one account key matches, then returns true.
+            for (byte[] key : accountKeys) {
+                if (Arrays.equals(key, scannedDe.getValue())) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
     }
 
-    /** Converts a presence device from the discovery result. */
-    public PresenceDevice toPresenceDevice() {
-        return new PresenceDevice.Builder(
-                // Use the public credential hash as the device Id.
-                String.valueOf(mPublicCredential.hashCode()),
-                mSalt,
-                mPublicCredential.getSecretId(),
-                mPublicCredential.getEncryptedMetadata())
-                .setRssi(mRssi)
-                .addMedium(NearbyDevice.Medium.BLE)
-                .build();
+    @VisibleForTesting
+    /** Gets presence {@link DataElement}s of the discovery result. */
+    public List<DataElement> getExtendedProperties() {
+        return mExtendedProperties;
+    }
+
+    private boolean identityMatches(List<PublicCredential> publicCredentials) {
+        if (mEncryptedIdentityTag.length == 0) {
+            return true;
+        }
+        for (PublicCredential publicCredential : publicCredentials) {
+            if (Arrays.equals(
+                    mEncryptedIdentityTag, publicCredential.getEncryptedMetadataKeyTag())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /** Builder for {@link PresenceDiscoveryResult}. */
@@ -105,9 +165,12 @@
 
         private PublicCredential mPublicCredential;
         private final List<Integer> mPresenceActions;
+        private final List<DataElement> mExtendedProperties;
+        private byte[] mEncryptedIdentityTag = new byte[0];
 
         public Builder() {
             mPresenceActions = new ArrayList<>();
+            mExtendedProperties = new ArrayList<>();
         }
 
         /** Sets the calibrated tx power for the discovery result. */
@@ -130,7 +193,18 @@
 
         /** Sets the public credential for the discovery result. */
         public Builder setPublicCredential(PublicCredential publicCredential) {
-            mPublicCredential = publicCredential;
+            if (publicCredential != null) {
+                mPublicCredential = publicCredential;
+            }
+            return this;
+        }
+
+        /** Sets the encrypted identity tag for the discovery result. Usually it is passed from
+         * {@link NearbyDeviceParcelable} and the tag is calculated with authenticity key when
+         * receiving an advertisement.
+         */
+        public Builder setEncryptedIdentityTag(byte[] encryptedIdentityTag) {
+            mEncryptedIdentityTag = encryptedIdentityTag;
             return this;
         }
 
@@ -140,10 +214,34 @@
             return this;
         }
 
+        /** Adds presence {@link DataElement}s of the discovery result. */
+        public Builder addExtendedProperties(DataElement dataElement) {
+            if (dataElement.getKey() == DataElement.DataType.ACTION) {
+                byte[] value = dataElement.getValue();
+                if (value.length == 1) {
+                    addPresenceAction(Byte.toUnsignedInt(value[0]));
+                } else {
+                    Log.e(TAG, "invalid action data element");
+                }
+            } else {
+                mExtendedProperties.add(dataElement);
+            }
+            return this;
+        }
+
+        /** Adds presence {@link DataElement}s of the discovery result. */
+        public Builder addExtendedProperties(@NonNull List<DataElement> dataElements) {
+            for (DataElement dataElement : dataElements) {
+                addExtendedProperties(dataElement);
+            }
+            return this;
+        }
+
         /** Builds a {@link PresenceDiscoveryResult}. */
         public PresenceDiscoveryResult build() {
             return new PresenceDiscoveryResult(
-                    mTxPower, mRssi, mSalt, mPresenceActions, mPublicCredential);
+                    mTxPower, mRssi, mSalt, mPresenceActions,
+                    mPublicCredential, mExtendedProperties, mEncryptedIdentityTag);
         }
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
new file mode 100644
index 0000000..8b2db50
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nearby.DataElement;
+import android.nearby.NearbyDevice;
+import android.nearby.NearbyManager;
+import android.nearby.PresenceDevice;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanCallback;
+import android.nearby.ScanRequest;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.Executors;
+
+/** PresenceManager is the class initiated in nearby service to handle presence related work. */
+public class PresenceManager {
+
+    final LocatorContextWrapper mLocatorContextWrapper;
+    final Locator mLocator;
+    private final IntentFilter mIntentFilter;
+
+    @VisibleForTesting
+    final ScanCallback mScanCallback =
+            new ScanCallback() {
+                @Override
+                public void onDiscovered(@NonNull NearbyDevice device) {
+                    Log.i(TAG, "[PresenceManager] discovered Device.");
+                    PresenceDevice presenceDevice = (PresenceDevice) device;
+                    List<DataElement> dataElements = presenceDevice.getExtendedProperties();
+                    for (DataElement dataElement : dataElements) {
+                        Log.i(TAG, "[PresenceManager] Data Element key "
+                                + dataElement.getKey());
+                        Log.i(TAG, "[PresenceManager] Data Element value "
+                                + Arrays.toString(dataElement.getValue()));
+                    }
+                }
+
+                @Override
+                public void onUpdated(@NonNull NearbyDevice device) {}
+
+                @Override
+                public void onLost(@NonNull NearbyDevice device) {}
+            };
+
+    private final BroadcastReceiver mScreenBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    NearbyManager manager = getNearbyManager();
+                    if (manager == null) {
+                        Log.e(TAG, "Nearby Manager is null");
+                        return;
+                    }
+                    if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+                        Log.d(TAG, "PresenceManager Start scan.");
+                        PublicCredential publicCredential =
+                                new PublicCredential.Builder(new byte[]{1}, new byte[]{1},
+                                        new byte[]{1}, new byte[]{1}, new byte[]{1}).build();
+                        PresenceScanFilter presenceScanFilter =
+                                new PresenceScanFilter.Builder()
+                                        .setMaxPathLoss(3)
+                                        .addCredential(publicCredential)
+                                        .addPresenceAction(1)
+                                        .addExtendedProperty(new DataElement(
+                                                DataElement.DataType.ACCOUNT_KEY_DATA,
+                                                new byte[16]))
+                                        .build();
+                        ScanRequest scanRequest =
+                                new ScanRequest.Builder()
+                                        .setScanType(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE)
+                                        .addScanFilter(presenceScanFilter)
+                                        .build();
+                        Log.d(
+                                TAG,
+                                String.format(
+                                        Locale.getDefault(),
+                                        "[PresenceManager] Start Presence scan with request: %s",
+                                        scanRequest.toString()));
+                        manager.startScan(
+                                scanRequest, Executors.newSingleThreadExecutor(), mScanCallback);
+                    } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                        Log.d(TAG, "PresenceManager Stop scan.");
+                        manager.stopScan(mScanCallback);
+                    }
+                }
+            };
+
+    public PresenceManager(LocatorContextWrapper contextWrapper) {
+        mLocatorContextWrapper = contextWrapper;
+        mLocator = mLocatorContextWrapper.getLocator();
+        mIntentFilter = new IntentFilter();
+    }
+
+    /** Null when the Nearby Service is not available. */
+    @Nullable
+    private NearbyManager getNearbyManager() {
+        return (NearbyManager)
+                mLocatorContextWrapper
+                        .getApplicationContext()
+                        .getSystemService(Context.NEARBY_SERVICE);
+    }
+
+    /** Function called when nearby service start. */
+    public void initiate() {
+        mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        mLocatorContextWrapper
+                .getContext()
+                .registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
index 67392ad..2b9fdb9 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
@@ -16,17 +16,25 @@
 
 package com.android.server.nearby.provider;
 
+import static com.android.server.nearby.NearbyService.TAG;
+import static com.android.server.nearby.presence.PresenceConstants.PRESENCE_UUID;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.le.AdvertiseCallback;
 import android.bluetooth.le.AdvertiseData;
 import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertisingSet;
+import android.bluetooth.le.AdvertisingSetCallback;
+import android.bluetooth.le.AdvertisingSetParameters;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.nearby.BroadcastCallback;
+import android.nearby.BroadcastRequest;
 import android.os.ParcelUuid;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.injector.Injector;
 
-import java.util.UUID;
 import java.util.concurrent.Executor;
 
 /**
@@ -46,13 +54,16 @@
 
     private BroadcastListener mBroadcastListener;
     private boolean mIsAdvertising;
-
+    @VisibleForTesting
+    AdvertisingSetCallback mAdvertisingSetCallback;
     BleBroadcastProvider(Injector injector, Executor executor) {
         mInjector = injector;
         mExecutor = executor;
+        mAdvertisingSetCallback = getAdvertisingSetCallback();
     }
 
-    void start(byte[] advertisementPackets, BroadcastListener listener) {
+    void start(@BroadcastRequest.BroadcastVersion int version, byte[] advertisementPackets,
+            BroadcastListener listener) {
         if (mIsAdvertising) {
             stop();
         }
@@ -63,23 +74,36 @@
                     mInjector.getBluetoothAdapter().getBluetoothLeAdvertiser();
             if (bluetoothLeAdvertiser != null) {
                 advertiseStarted = true;
-                AdvertiseSettings settings =
-                        new AdvertiseSettings.Builder()
-                                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
-                                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
-                                .setConnectable(true)
-                                .build();
-
-                // TODO(b/230538655) Use empty data until Presence V1 protocol is implemented.
-                ParcelUuid emptyParcelUuid = new ParcelUuid(new UUID(0L, 0L));
-                byte[] emptyAdvertisementPackets = new byte[0];
                 AdvertiseData advertiseData =
                         new AdvertiseData.Builder()
-                                .addServiceData(emptyParcelUuid, emptyAdvertisementPackets).build();
+                                .addServiceData(new ParcelUuid(PRESENCE_UUID),
+                                        advertisementPackets).build();
                 try {
                     mBroadcastListener = listener;
-                    bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, this);
+                    switch (version) {
+                        case BroadcastRequest.PRESENCE_VERSION_V0:
+                            bluetoothLeAdvertiser.startAdvertising(getAdvertiseSettings(),
+                                    advertiseData, this);
+                            break;
+                        case BroadcastRequest.PRESENCE_VERSION_V1:
+                            if (adapter.isLeExtendedAdvertisingSupported()) {
+                                bluetoothLeAdvertiser.startAdvertisingSet(
+                                        getAdvertisingSetParameters(),
+                                        advertiseData,
+                                        null, null, null, mAdvertisingSetCallback);
+                            } else {
+                                Log.w(TAG, "Failed to start advertising set because the chipset"
+                                        + " does not supports LE Extended Advertising feature.");
+                                advertiseStarted = false;
+                            }
+                            break;
+                        default:
+                            Log.w(TAG, "Failed to start advertising set because the advertisement"
+                                    + " is wrong.");
+                            advertiseStarted = false;
+                    }
                 } catch (NullPointerException | IllegalStateException | SecurityException e) {
+                    Log.w(TAG, "Failed to start advertising.", e);
                     advertiseStarted = false;
                 }
             }
@@ -97,6 +121,7 @@
                         mInjector.getBluetoothAdapter().getBluetoothLeAdvertiser();
                 if (bluetoothLeAdvertiser != null) {
                     bluetoothLeAdvertiser.stopAdvertising(this);
+                    bluetoothLeAdvertiser.stopAdvertisingSet(mAdvertisingSetCallback);
                 }
             }
             mBroadcastListener = null;
@@ -120,4 +145,41 @@
             mBroadcastListener.onStatusChanged(BroadcastCallback.STATUS_FAILURE);
         }
     }
+
+    private static AdvertiseSettings getAdvertiseSettings() {
+        return new AdvertiseSettings.Builder()
+                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
+                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+                .setConnectable(true)
+                .build();
+    }
+
+    private static AdvertisingSetParameters getAdvertisingSetParameters() {
+        return new AdvertisingSetParameters.Builder()
+                .setInterval(AdvertisingSetParameters.INTERVAL_MEDIUM)
+                .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_MEDIUM)
+                .setIncludeTxPower(true)
+                .setConnectable(true)
+                .build();
+    }
+
+    private AdvertisingSetCallback getAdvertisingSetCallback() {
+        return new AdvertisingSetCallback() {
+            @Override
+            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet,
+                    int txPower, int status) {
+                if (status == AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+                    if (mBroadcastListener != null) {
+                        mBroadcastListener.onStatusChanged(BroadcastCallback.STATUS_OK);
+                    }
+                    mIsAdvertising = true;
+                } else {
+                    Log.e(TAG, "Starts advertising failed in status " + status);
+                    if (mBroadcastListener != null) {
+                        mBroadcastListener.onStatusChanged(BroadcastCallback.STATUS_FAILURE);
+                    }
+                }
+            }
+        };
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
index e8aea79..d828ef9 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
@@ -27,16 +27,22 @@
 import android.bluetooth.le.ScanResult;
 import android.bluetooth.le.ScanSettings;
 import android.content.Context;
+import android.nearby.DataElement;
 import android.nearby.NearbyDevice;
 import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PresenceDevice;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
 import android.nearby.ScanRequest;
 import android.os.ParcelUuid;
 import android.util.Log;
 
 import com.android.server.nearby.common.bluetooth.fastpair.Constants;
 import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.ExtendedAdvertisement;
 import com.android.server.nearby.presence.PresenceConstants;
 import com.android.server.nearby.util.ForegroundThread;
+import com.android.server.nearby.util.encryption.CryptorImpIdentityV1;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -57,6 +63,15 @@
     // Don't block the thread as it may be used by other services.
     private static final Executor NEARBY_EXECUTOR = ForegroundThread.getExecutor();
     private final Injector mInjector;
+    private android.bluetooth.le.ScanCallback mScanCallbackLegacy =
+            new android.bluetooth.le.ScanCallback() {
+                @Override
+                public void onScanResult(int callbackType, ScanResult scanResult) {
+                }
+                @Override
+                public void onScanFailed(int errorCode) {
+                }
+            };
     private android.bluetooth.le.ScanCallback mScanCallback =
             new android.bluetooth.le.ScanCallback() {
                 @Override
@@ -81,7 +96,8 @@
                             } else {
                                 byte[] presenceData = serviceDataMap.get(PRESENCE_UUID);
                                 if (presenceData != null) {
-                                    builder.setData(serviceDataMap.get(PRESENCE_UUID));
+                                    setPresenceDevice(presenceData, builder, deviceName,
+                                            scanResult.getRssi());
                                 }
                             }
                         }
@@ -91,7 +107,7 @@
 
                 @Override
                 public void onScanFailed(int errorCode) {
-                    Log.w(TAG, "BLE Scan failed with error code " + errorCode);
+                    Log.w(TAG, "BLE 5.0 Scan failed with error code " + errorCode);
                 }
             };
 
@@ -100,12 +116,39 @@
         mInjector = injector;
     }
 
+    private static PresenceDevice getPresenceDevice(ExtendedAdvertisement advertisement,
+            String deviceName, int rssi) {
+        // TODO(238458326): After implementing encryption, use real data.
+        byte[] secretIdBytes = new byte[0];
+        PresenceDevice.Builder builder =
+                new PresenceDevice.Builder(
+                        String.valueOf(advertisement.hashCode()),
+                        advertisement.getSalt(),
+                        secretIdBytes,
+                        advertisement.getIdentity())
+                        .addMedium(NearbyDevice.Medium.BLE)
+                        .setName(deviceName)
+                        .setRssi(rssi);
+        for (int i : advertisement.getActions()) {
+            builder.addExtendedProperty(new DataElement(DataElement.DataType.ACTION,
+                    new byte[]{(byte) i}));
+        }
+        for (DataElement dataElement : advertisement.getDataElements()) {
+            builder.addExtendedProperty(dataElement);
+        }
+        return builder.build();
+    }
+
     private static List<ScanFilter> getScanFilters() {
         List<ScanFilter> scanFilterList = new ArrayList<>();
         scanFilterList.add(
                 new ScanFilter.Builder()
                         .setServiceData(FAST_PAIR_UUID, new byte[]{0}, new byte[]{0})
                         .build());
+        scanFilterList.add(
+                new ScanFilter.Builder()
+                        .setServiceData(PRESENCE_UUID, new byte[]{0}, new byte[]{0})
+                        .build());
         return scanFilterList;
     }
 
@@ -130,8 +173,9 @@
     @Override
     protected void onStart() {
         if (isBleAvailable()) {
-            Log.d(TAG, "BleDiscoveryProvider started.");
-            startScan(getScanFilters(), getScanSettings(), mScanCallback);
+            Log.d(TAG, "BleDiscoveryProvider started");
+            startScan(getScanFilters(), getScanSettings(/* legacy= */ false), mScanCallback);
+            startScan(getScanFilters(), getScanSettings(/* legacy= */ true), mScanCallbackLegacy);
             return;
         }
         Log.w(TAG, "Cannot start BleDiscoveryProvider because Ble is not available.");
@@ -148,6 +192,10 @@
         }
         Log.v(TAG, "Ble scan stopped.");
         bluetoothLeScanner.stopScan(mScanCallback);
+        bluetoothLeScanner.stopScan(mScanCallbackLegacy);
+        if (mScanFilters != null) {
+            mScanFilters.clear();
+        }
     }
 
     @Override
@@ -179,7 +227,7 @@
         }
     }
 
-    private ScanSettings getScanSettings() {
+    private ScanSettings getScanSettings(boolean legacy) {
         int bleScanMode = ScanSettings.SCAN_MODE_LOW_POWER;
         switch (mController.getProviderScanMode()) {
             case ScanRequest.SCAN_MODE_LOW_LATENCY:
@@ -195,11 +243,37 @@
                 bleScanMode = ScanSettings.SCAN_MODE_OPPORTUNISTIC;
                 break;
         }
-        return new ScanSettings.Builder().setScanMode(bleScanMode).build();
+        return new ScanSettings.Builder().setScanMode(bleScanMode).setLegacy(legacy).build();
     }
 
     @VisibleForTesting
     ScanCallback getScanCallback() {
         return mScanCallback;
     }
+
+    private void setPresenceDevice(byte[] data, NearbyDeviceParcelable.Builder builder,
+            String deviceName, int rssi) {
+        for (android.nearby.ScanFilter scanFilter : mScanFilters) {
+            if (scanFilter instanceof PresenceScanFilter) {
+                // Iterate all possible authenticity key and identity combinations to decrypt
+                // advertisement
+                PresenceScanFilter presenceFilter = (PresenceScanFilter) scanFilter;
+                for (PublicCredential credential : presenceFilter.getCredentials()) {
+                    ExtendedAdvertisement advertisement =
+                            ExtendedAdvertisement.fromBytes(data, credential);
+                    if (advertisement == null) {
+                        continue;
+                    }
+                    if (CryptorImpIdentityV1.getInstance().verify(
+                            advertisement.getIdentity(),
+                            credential.getEncryptedMetadataKeyTag())) {
+                        builder.setPresenceDevice(getPresenceDevice(advertisement, deviceName,
+                                rssi));
+                        builder.setEncryptionKeyTag(credential.getEncryptedMetadataKeyTag());
+                        return;
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
index 3fffda5..400f936 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
@@ -16,6 +16,9 @@
 
 package com.android.server.nearby.provider;
 
+import static com.android.server.nearby.NearbyService.SUPPORT_TEST_APP;
+
+import android.annotation.Nullable;
 import android.content.Context;
 import android.nearby.BroadcastCallback;
 import android.nearby.BroadcastRequest;
@@ -27,6 +30,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.NearbyConfiguration;
 import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.presence.Advertisement;
+import com.android.server.nearby.presence.ExtendedAdvertisement;
 import com.android.server.nearby.presence.FastAdvertisement;
 import com.android.server.nearby.util.ForegroundThread;
 
@@ -66,10 +71,12 @@
     public void startBroadcast(BroadcastRequest broadcastRequest, IBroadcastListener listener) {
         synchronized (mLock) {
             mExecutor.execute(() -> {
-                NearbyConfiguration configuration = new NearbyConfiguration();
-                if (!configuration.isPresenceBroadcastLegacyEnabled()) {
-                    reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
-                    return;
+                if (!SUPPORT_TEST_APP) {
+                    NearbyConfiguration configuration = new NearbyConfiguration();
+                    if (!configuration.isPresenceBroadcastLegacyEnabled()) {
+                        reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
+                        return;
+                    }
                 }
                 if (broadcastRequest.getType() != BroadcastRequest.BROADCAST_TYPE_NEARBY_PRESENCE) {
                     reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
@@ -77,25 +84,37 @@
                 }
                 PresenceBroadcastRequest presenceBroadcastRequest =
                         (PresenceBroadcastRequest) broadcastRequest;
-                if (presenceBroadcastRequest.getVersion() != BroadcastRequest.PRESENCE_VERSION_V0) {
+                Advertisement advertisement = getAdvertisement(presenceBroadcastRequest);
+                if (advertisement == null) {
+                    Log.e(TAG, "Failed to start broadcast because broadcastRequest is illegal.");
                     reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
                     return;
                 }
-                FastAdvertisement fastAdvertisement = FastAdvertisement.createFromRequest(
-                        presenceBroadcastRequest);
-                byte[] advertisementPackets = fastAdvertisement.toBytes();
                 mBroadcastListener = listener;
-                mBleBroadcastProvider.start(advertisementPackets, this);
+                mBleBroadcastProvider.start(presenceBroadcastRequest.getVersion(),
+                        advertisement.toBytes(), this);
             });
         }
     }
 
+    @Nullable
+    private Advertisement getAdvertisement(PresenceBroadcastRequest request) {
+        switch (request.getVersion()) {
+            case BroadcastRequest.PRESENCE_VERSION_V0:
+                return FastAdvertisement.createFromRequest(request);
+            case BroadcastRequest.PRESENCE_VERSION_V1:
+                return ExtendedAdvertisement.createFromRequest(request);
+            default:
+                return null;
+        }
+    }
+
     /**
      * Stops the nearby broadcast.
      */
     public void stopBroadcast(IBroadcastListener listener) {
         synchronized (mLock) {
-            if (!mNearbyConfiguration.isPresenceBroadcastLegacyEnabled()) {
+            if (!SUPPORT_TEST_APP && !mNearbyConfiguration.isPresenceBroadcastLegacyEnabled()) {
                 reportBroadcastStatus(listener, BroadcastCallback.STATUS_FAILURE);
                 return;
             }
diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java b/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
index 5077ffe..00e1cb6 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreCommunication.java
@@ -19,15 +19,17 @@
 import static com.android.server.nearby.NearbyService.TAG;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.hardware.location.ContextHubClient;
 import android.hardware.location.ContextHubClientCallback;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubManager;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
 import android.util.Log;
 
-import com.android.server.nearby.injector.ContextHubManagerAdapter;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.injector.Injector;
 
 import com.google.common.base.Preconditions;
@@ -63,14 +65,16 @@
     }
 
     private final Injector mInjector;
+    private final Context mContext;
     private final Executor mExecutor;
 
     private boolean mStarted = false;
     @Nullable private ContextHubCommsCallback mCallback;
     @Nullable private ContextHubClient mContextHubClient;
 
-    public ChreCommunication(Injector injector, Executor executor) {
+    public ChreCommunication(Injector injector, Context context, Executor executor) {
         mInjector = injector;
+        mContext = context;
         mExecutor = executor;
     }
 
@@ -86,12 +90,12 @@
      *     contexthub.
      */
     public synchronized void start(ContextHubCommsCallback callback, Set<Long> nanoAppIds) {
-        ContextHubManagerAdapter manager = mInjector.getContextHubManagerAdapter();
+        ContextHubManager manager = mInjector.getContextHubManager();
         if (manager == null) {
             Log.e(TAG, "ContexHub not available in this device");
             return;
         } else {
-            Log.i(TAG, "Start ChreCommunication");
+            Log.i(TAG, "[ChreCommunication] Start ChreCommunication");
         }
         Preconditions.checkNotNull(callback);
         Preconditions.checkArgument(!nanoAppIds.isEmpty());
@@ -172,7 +176,8 @@
         mCallback.onNanoAppRestart(nanoAppId);
     }
 
-    private static String contextHubTransactionResultToString(int result) {
+    @VisibleForTesting
+    static String contextHubTransactionResultToString(int result) {
         switch (result) {
             case ContextHubTransaction.RESULT_SUCCESS:
                 return "RESULT_SUCCESS";
@@ -207,13 +212,13 @@
         private final ContextHubInfo mQueriedContextHub;
         private final List<ContextHubInfo> mContextHubs;
         private final Set<Long> mNanoAppIds;
-        private final ContextHubManagerAdapter mManager;
+        private final ContextHubManager mManager;
 
         OnQueryCompleteListener(
                 ContextHubInfo queriedContextHub,
                 List<ContextHubInfo> contextHubs,
                 Set<Long> nanoAppIds,
-                ContextHubManagerAdapter manager) {
+                ContextHubManager manager) {
             this.mQueriedContextHub = queriedContextHub;
             this.mContextHubs = contextHubs;
             this.mNanoAppIds = nanoAppIds;
@@ -238,14 +243,13 @@
                                 TAG,
                                 String.format(
                                         "Found valid contexthub: %s", mQueriedContextHub.getId()));
-                        mContextHubClient =
-                                mManager.createClient(
-                                        mQueriedContextHub, ChreCommunication.this, mExecutor);
+                        mContextHubClient = mManager.createClient(mContext, mQueriedContextHub,
+                                mExecutor, ChreCommunication.this);
                         mCallback.started(true);
                         return;
                     }
                 }
-                Log.e(
+                Log.i(
                         TAG,
                         String.format(
                                 "Didn't find the nanoapp on contexthub: %s",
diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
index f20c6d8..105051a 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
@@ -20,10 +20,19 @@
 
 import static com.android.server.nearby.NearbyService.TAG;
 
+import static service.proto.Blefilter.DataElement.ElementType.DE_BATTERY_STATUS;
+import static service.proto.Blefilter.DataElement.ElementType.DE_CONNECTION_STATUS;
+import static service.proto.Blefilter.DataElement.ElementType.DE_FAST_PAIR_ACCOUNT_KEY;
+
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.location.NanoAppMessage;
+import android.nearby.DataElement;
 import android.nearby.NearbyDevice;
 import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PresenceDevice;
 import android.nearby.PresenceScanFilter;
 import android.nearby.PublicCredential;
 import android.nearby.ScanFilter;
@@ -31,7 +40,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.ByteString;
 
 import java.util.Collections;
 import java.util.concurrent.Executor;
@@ -47,34 +56,56 @@
     @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3;
     /** @hide */
     @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4;
+    /** @hide */
+    @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_CONFIG = 5;
 
     private static final int PRESENCE_UUID = 0xFCF1;
+    private static final int FP_ACCOUNT_KEY_LENGTH = 16;
 
-    private ChreCommunication mChreCommunication;
-    private ChreCallback mChreCallback;
+    private final ChreCommunication mChreCommunication;
+    private final ChreCallback mChreCallback;
     private boolean mChreStarted = false;
     private Blefilter.BleFilters mFilters = null;
-    private int mFilterId;
+    private Context mContext;
+    private final IntentFilter mIntentFilter;
+
+    private final BroadcastReceiver mScreenBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    Boolean screenOn = intent.getAction().equals(Intent.ACTION_SCREEN_ON)
+                            || intent.getAction().equals(Intent.ACTION_USER_PRESENT);
+                    Log.d(TAG, String.format(
+                            "[ChreDiscoveryProvider] update nanoapp screen status: %B", screenOn));
+                    sendScreenUpdate(screenOn);
+                }
+            };
 
     public ChreDiscoveryProvider(
             Context context, ChreCommunication chreCommunication, Executor executor) {
         super(context, executor);
+        mContext = context;
         mChreCommunication = chreCommunication;
         mChreCallback = new ChreCallback();
-        mFilterId = 0;
+        mIntentFilter = new IntentFilter();
+    }
+
+    /** Initialize the CHRE discovery provider. */
+    public void init() {
+        mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID));
     }
 
     @Override
     protected void onStart() {
         Log.d(TAG, "Start CHRE scan");
-        mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID));
         updateFilters();
     }
 
     @Override
     protected void onStop() {
-        mChreStarted = false;
-        mChreCommunication.stop();
+        Log.d(TAG, "Stop CHRE scan");
+        mScanFilters.clear();
+        updateFilters();
     }
 
     @Override
@@ -95,22 +126,47 @@
         Blefilter.BleFilters.Builder filtersBuilder = Blefilter.BleFilters.newBuilder();
         for (ScanFilter scanFilter : mScanFilters) {
             PresenceScanFilter presenceScanFilter = (PresenceScanFilter) scanFilter;
-            Blefilter.BleFilter filter =
-                    Blefilter.BleFilter.newBuilder()
-                            .setId(mFilterId)
-                            .setUuid(PRESENCE_UUID)
-                            .setIntent(presenceScanFilter.getPresenceActions().get(0))
-                            .build();
-            filtersBuilder.addFilter(filter);
-            mFilterId++;
+            Blefilter.BleFilter.Builder filterBuilder = Blefilter.BleFilter.newBuilder();
+            for (PublicCredential credential : presenceScanFilter.getCredentials()) {
+                filterBuilder.addCertificate(toProtoPublicCredential(credential));
+            }
+            for (DataElement dataElement : presenceScanFilter.getExtendedProperties()) {
+                if (dataElement.getKey() == DataElement.DataType.ACCOUNT_KEY_DATA) {
+                    filterBuilder.addDataElement(toProtoDataElement(dataElement));
+                }
+            }
+            if (!presenceScanFilter.getPresenceActions().isEmpty()) {
+                filterBuilder.setIntent(presenceScanFilter.getPresenceActions().get(0));
+            }
+            filtersBuilder.addFilter(filterBuilder.build());
         }
-        mFilters = filtersBuilder.build();
         if (mChreStarted) {
-            sendFilters(mFilters);
+            sendFilters(filtersBuilder.build());
             mFilters = null;
         }
     }
 
+    private Blefilter.PublicateCertificate toProtoPublicCredential(PublicCredential credential) {
+        Log.d(TAG, String.format("Returns a PublicCertificate with authenticity key size %d and"
+                + " encrypted metadata key tag size %d", credential.getAuthenticityKey().length,
+                credential.getEncryptedMetadataKeyTag().length));
+        return Blefilter.PublicateCertificate.newBuilder()
+                .setAuthenticityKey(ByteString.copyFrom(credential.getAuthenticityKey()))
+                .setMetadataEncryptionKeyTag(
+                        ByteString.copyFrom(credential.getEncryptedMetadataKeyTag()))
+                .build();
+    }
+
+    private Blefilter.DataElement toProtoDataElement(DataElement dataElement) {
+        return Blefilter.DataElement.newBuilder()
+                        .setKey(
+                                Blefilter.DataElement.ElementType
+                                        .DE_FAST_PAIR_ACCOUNT_KEY)
+                        .setValue(ByteString.copyFrom(dataElement.getValue()))
+                        .setValueLength(FP_ACCOUNT_KEY_LENGTH)
+                        .build();
+    }
+
     private void sendFilters(Blefilter.BleFilters filters) {
         NanoAppMessage message =
                 NanoAppMessage.createMessageToNanoApp(
@@ -120,6 +176,16 @@
         }
     }
 
+    private void sendScreenUpdate(Boolean screenOn) {
+        Blefilter.BleConfig config = Blefilter.BleConfig.newBuilder().setScreenOn(screenOn).build();
+        NanoAppMessage message =
+                NanoAppMessage.createMessageToNanoApp(
+                        NANOAPP_ID, NANOAPP_MESSAGE_TYPE_CONFIG, config.toByteArray());
+        if (!mChreCommunication.sendMessageToNanoApp(message)) {
+            Log.e(TAG, "Failed to send config to CHRE.");
+        }
+    }
+
     private class ChreCallback implements ChreCommunication.ContextHubCommsCallback {
 
         @Override
@@ -127,6 +193,10 @@
             if (success) {
                 synchronized (ChreDiscoveryProvider.this) {
                     Log.i(TAG, "CHRE communication started");
+                    mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+                    mIntentFilter.addAction(Intent.ACTION_USER_PRESENT);
+                    mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+                    mContext.registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
                     mChreStarted = true;
                     if (mFilters != null) {
                         sendFilters(mFilters);
@@ -163,15 +233,81 @@
                     Blefilter.BleFilterResults results =
                             Blefilter.BleFilterResults.parseFrom(message.getMessageBody());
                     for (Blefilter.BleFilterResult filterResult : results.getResultList()) {
-                        Blefilter.PublicCredential credential = filterResult.getPublicCredential();
+                        // TODO(b/234653356): There are some duplicate fields set both in
+                        //  PresenceDevice and NearbyDeviceParcelable, cleanup is needed.
+                        byte[] salt = {1};
+                        byte[] secretId = {1};
+                        byte[] authenticityKey = {1};
+                        byte[] publicKey = {1};
+                        byte[] encryptedMetaData = {1};
+                        byte[] encryptedMetaDataTag = {1};
+                        if (filterResult.hasPublicCredential()) {
+                            Blefilter.PublicCredential credential =
+                                    filterResult.getPublicCredential();
+                            secretId = credential.getSecretId().toByteArray();
+                            authenticityKey = credential.getAuthenticityKey().toByteArray();
+                            publicKey = credential.getPublicKey().toByteArray();
+                            encryptedMetaData = credential.getEncryptedMetadata().toByteArray();
+                            encryptedMetaDataTag =
+                                    credential.getEncryptedMetadataTag().toByteArray();
+                        }
+                        PresenceDevice.Builder presenceDeviceBuilder =
+                                new PresenceDevice.Builder(
+                                                String.valueOf(filterResult.hashCode()),
+                                                salt,
+                                                secretId,
+                                                encryptedMetaData)
+                                        .setRssi(filterResult.getRssi())
+                                        .addMedium(NearbyDevice.Medium.BLE);
+                        // Data Elements reported from nanoapp added to Data Elements.
+                        // i.e. Fast Pair account keys, connection status and battery
+                        for (Blefilter.DataElement element : filterResult.getDataElementList()) {
+                            addDataElementsToPresenceDevice(element, presenceDeviceBuilder);
+                        }
+                        // BlE address appended to Data Element.
+                        if (filterResult.hasBluetoothAddress()) {
+                            presenceDeviceBuilder.addExtendedProperty(
+                                    new DataElement(
+                                            DataElement.DataType.BLE_ADDRESS,
+                                            filterResult.getBluetoothAddress().toByteArray()));
+                        }
+                        // BlE TX Power appended to Data Element.
+                        if (filterResult.hasTxPower()) {
+                            presenceDeviceBuilder.addExtendedProperty(
+                                    new DataElement(
+                                            DataElement.DataType.TX_POWER,
+                                            new byte[]{(byte) filterResult.getTxPower()}));
+                        }
+                        // BLE Service data appended to Data Elements.
+                        if (filterResult.hasBleServiceData()) {
+                            // Retrieves the length of the service data from the first byte,
+                            // and then skips the first byte and returns data[1 .. dataLength)
+                            // as the DataElement value.
+                            int dataLength = Byte.toUnsignedInt(
+                                    filterResult.getBleServiceData().byteAt(0));
+                            presenceDeviceBuilder.addExtendedProperty(
+                                    new DataElement(
+                                            DataElement.DataType.BLE_SERVICE_DATA,
+                                            filterResult.getBleServiceData()
+                                                    .substring(1, 1 + dataLength).toByteArray()));
+                        }
+                        // Add action
+                        if (filterResult.hasIntent()) {
+                            presenceDeviceBuilder.addExtendedProperty(
+                                    new DataElement(
+                                            DataElement.DataType.ACTION,
+                                            new byte[]{(byte) filterResult.getIntent()}));
+                        }
+
                         PublicCredential publicCredential =
                                 new PublicCredential.Builder(
-                                                credential.getSecretId().toByteArray(),
-                                                credential.getAuthenticityKey().toByteArray(),
-                                                credential.getPublicKey().toByteArray(),
-                                                credential.getEncryptedMetadata().toByteArray(),
-                                                credential.getEncryptedMetadataTag().toByteArray())
+                                                secretId,
+                                                authenticityKey,
+                                                publicKey,
+                                                encryptedMetaData,
+                                                encryptedMetaDataTag)
                                         .build();
+
                         NearbyDeviceParcelable device =
                                 new NearbyDeviceParcelable.Builder()
                                         .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
@@ -180,15 +316,40 @@
                                         .setRssi(filterResult.getRssi())
                                         .setAction(filterResult.getIntent())
                                         .setPublicCredential(publicCredential)
+                                        .setPresenceDevice(presenceDeviceBuilder.build())
+                                        .setEncryptionKeyTag(encryptedMetaDataTag)
                                         .build();
                         mExecutor.execute(() -> mListener.onNearbyDeviceDiscovered(device));
                     }
-                } catch (InvalidProtocolBufferException e) {
-                    Log.e(
-                            TAG,
-                            String.format("Failed to decode the filter result %s", e.toString()));
+                } catch (Exception e) {
+                    Log.e(TAG, String.format("Failed to decode the filter result %s", e));
                 }
             }
         }
+
+        private void addDataElementsToPresenceDevice(Blefilter.DataElement element,
+                PresenceDevice.Builder presenceDeviceBuilder) {
+            int endIndex = element.hasValueLength() ? element.getValueLength() :
+                    element.getValue().size();
+            switch (element.getKey()) {
+                case DE_FAST_PAIR_ACCOUNT_KEY:
+                    presenceDeviceBuilder.addExtendedProperty(
+                            new DataElement(DataElement.DataType.ACCOUNT_KEY_DATA,
+                                    element.getValue().substring(0, endIndex).toByteArray()));
+                    break;
+                case DE_CONNECTION_STATUS:
+                    presenceDeviceBuilder.addExtendedProperty(
+                            new DataElement(DataElement.DataType.CONNECTION_STATUS,
+                                    element.getValue().substring(0, endIndex).toByteArray()));
+                    break;
+                case DE_BATTERY_STATUS:
+                    presenceDeviceBuilder.addExtendedProperty(
+                            new DataElement(DataElement.DataType.BATTERY,
+                                    element.getValue().substring(0, endIndex).toByteArray()));
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 }
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
index bdeab51..85b1228 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.injector.Injector;
 import com.android.server.nearby.metrics.NearbyMetrics;
 import com.android.server.nearby.presence.PresenceDiscoveryResult;
@@ -50,7 +51,6 @@
 
 /** Manages all aspects of discovery providers. */
 public class DiscoveryProviderManager implements AbstractDiscoveryProvider.Listener {
-
     protected final Object mLock = new Object();
     private final Context mContext;
     private final BleDiscoveryProvider mBleDiscoveryProvider;
@@ -92,10 +92,9 @@
                                                     scanFilter.getType()
                                                             == SCAN_TYPE_NEARBY_PRESENCE)
                                     .collect(Collectors.toList());
-                    Log.i(
-                            TAG,
-                            String.format("match with filters size: %d", presenceFilters.size()));
                     if (!presenceFilterMatches(nearbyDevice, presenceFilters)) {
+                        Log.d(TAG, "presence filter does not match for "
+                                + "the scanned Presence Device");
                         continue;
                     }
                 }
@@ -119,18 +118,35 @@
         Executor executor = Executors.newSingleThreadExecutor();
         mChreDiscoveryProvider =
                 new ChreDiscoveryProvider(
-                        mContext, new ChreCommunication(injector, executor), executor);
+                        mContext, new ChreCommunication(injector, mContext, executor), executor);
         mScanTypeScanListenerRecordMap = new HashMap<>();
         mInjector = injector;
     }
 
+    /** Called after boot completed. */
+    public void init() {
+        if (mInjector.getContextHubManager() != null) {
+            mChreDiscoveryProvider.init();
+        }
+        mChreDiscoveryProvider.getController().setListener(this);
+    }
+
     /**
      * Registers the listener in the manager and starts scan according to the requested scan mode.
      */
     public boolean registerScanListener(ScanRequest scanRequest, IScanListener listener,
             CallerIdentity callerIdentity) {
         synchronized (mLock) {
+            ScanListenerDeathRecipient deathRecipient = (listener != null)
+                    ? new ScanListenerDeathRecipient(listener) : null;
             IBinder listenerBinder = listener.asBinder();
+            if (listenerBinder != null && deathRecipient != null) {
+                try {
+                    listenerBinder.linkToDeath(deathRecipient, 0);
+                } catch (RemoteException e) {
+                    throw new IllegalArgumentException("Can't link to scan listener's death");
+                }
+            }
             if (mScanTypeScanListenerRecordMap.containsKey(listener.asBinder())) {
                 ScanRequest savedScanRequest =
                         mScanTypeScanListenerRecordMap.get(listenerBinder).getScanRequest();
@@ -140,7 +156,7 @@
                 }
             }
             ScanListenerRecord scanListenerRecord =
-                    new ScanListenerRecord(scanRequest, listener, callerIdentity);
+                    new ScanListenerRecord(scanRequest, listener, callerIdentity, deathRecipient);
             mScanTypeScanListenerRecordMap.put(listenerBinder, scanListenerRecord);
 
             if (!startProviders(scanRequest)) {
@@ -172,6 +188,10 @@
 
             ScanListenerRecord removedRecord =
                     mScanTypeScanListenerRecordMap.remove(listenerBinder);
+            ScanListenerDeathRecipient deathRecipient = removedRecord.getDeathRecipient();
+            if (listenerBinder != null && deathRecipient != null) {
+                listenerBinder.unlinkToDeath(removedRecord.getDeathRecipient(), 0);
+            }
             Log.v(TAG, "DiscoveryProviderManager unregistered scan listener.");
             NearbyMetrics.logScanStopped(removedRecord.hashCode(), removedRecord.getScanRequest());
             if (mScanTypeScanListenerRecordMap.isEmpty()) {
@@ -211,26 +231,34 @@
                     && scanRequest.getScanType() == SCAN_TYPE_NEARBY_PRESENCE) {
                 startChreProvider();
             } else {
-                startBleProvider(scanRequest);
+                startBleProvider();
             }
             return true;
         }
         return false;
     }
 
-    private void startBleProvider(ScanRequest scanRequest) {
+    private void startBleProvider() {
         if (!mBleDiscoveryProvider.getController().isStarted()) {
             Log.d(TAG, "DiscoveryProviderManager starts Ble scanning.");
-            mBleDiscoveryProvider.getController().start();
             mBleDiscoveryProvider.getController().setListener(this);
-            mBleDiscoveryProvider.getController().setProviderScanMode(scanRequest.getScanMode());
+            mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+            mBleDiscoveryProvider.getController().setProviderScanFilters(
+                    getPresenceScanFilters());
+            mBleDiscoveryProvider.getController().start();
         }
     }
 
-    private void startChreProvider() {
+    @VisibleForTesting
+    void startChreProvider() {
         Log.d(TAG, "DiscoveryProviderManager starts CHRE scanning.");
+        mChreDiscoveryProvider.getController().setProviderScanFilters(getPresenceScanFilters());
+        mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
+        mChreDiscoveryProvider.getController().start();
+    }
+
+    private List<ScanFilter> getPresenceScanFilters() {
         synchronized (mLock) {
-            mChreDiscoveryProvider.getController().setListener(this);
             List<ScanFilter> scanFilters = new ArrayList();
             for (IBinder listenerBinder : mScanTypeScanListenerRecordMap.keySet()) {
                 ScanListenerRecord record = mScanTypeScanListenerRecordMap.get(listenerBinder);
@@ -242,9 +270,7 @@
                                 .collect(Collectors.toList());
                 scanFilters.addAll(presenceFilters);
             }
-            mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
-            mChreDiscoveryProvider.getController().setProviderScanMode(mScanMode);
-            mChreDiscoveryProvider.getController().start();
+            return scanFilters;
         }
     }
 
@@ -261,7 +287,8 @@
         mChreDiscoveryProvider.getController().stop();
     }
 
-    private void invalidateProviderScanMode() {
+    @VisibleForTesting
+    void invalidateProviderScanMode() {
         if (mBleDiscoveryProvider.getController().isStarted()) {
             mBleDiscoveryProvider.getController().setProviderScanMode(mScanMode);
         } else {
@@ -272,7 +299,8 @@
         }
     }
 
-    private static boolean presenceFilterMatches(
+    @VisibleForTesting
+    static boolean presenceFilterMatches(
             NearbyDeviceParcelable device, List<ScanFilter> scanFilters) {
         if (scanFilters.isEmpty()) {
             return true;
@@ -287,6 +315,20 @@
         return false;
     }
 
+    class ScanListenerDeathRecipient implements IBinder.DeathRecipient {
+        public IScanListener listener;
+
+        ScanListenerDeathRecipient(IScanListener listener) {
+            this.listener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Binder is dead - unregistering scan listener");
+            unregisterScanListener(listener);
+        }
+    }
+
     private static class ScanListenerRecord {
 
         private final ScanRequest mScanRequest;
@@ -295,11 +337,14 @@
 
         private final CallerIdentity mCallerIdentity;
 
+        private final ScanListenerDeathRecipient mDeathRecipient;
+
         ScanListenerRecord(ScanRequest scanRequest, IScanListener iScanListener,
-                CallerIdentity callerIdentity) {
+                CallerIdentity callerIdentity, ScanListenerDeathRecipient deathRecipient) {
             mScanListener = iScanListener;
             mScanRequest = scanRequest;
             mCallerIdentity = callerIdentity;
+            mDeathRecipient = deathRecipient;
         }
 
         IScanListener getScanListener() {
@@ -314,6 +359,10 @@
             return mCallerIdentity;
         }
 
+        ScanListenerDeathRecipient getDeathRecipient()  {
+            return mDeathRecipient;
+        }
+
         @Override
         public boolean equals(Object other) {
             if (other instanceof ScanListenerRecord) {
diff --git a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
index 0f99a2f..d925f07 100644
--- a/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/FastPairDataProvider.java
@@ -30,7 +30,7 @@
 
 import androidx.annotation.WorkerThread;
 
-import com.android.server.nearby.common.bloomfilter.BloomFilter;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
 
 import java.util.ArrayList;
@@ -80,6 +80,11 @@
         }
     }
 
+    @VisibleForTesting
+    void setProxyDataProvider(ProxyFastPairDataProvider proxyFastPairDataProvider) {
+        this.mProxyFastPairDataProvider = proxyFastPairDataProvider;
+    }
+
     /**
      * Loads FastPairAntispoofKeyDeviceMetadata.
      *
@@ -136,14 +141,6 @@
     }
 
     /**
-     * Get recognized device from bloom filter.
-     */
-    public Data.FastPairDeviceWithAccountKey getRecognizedDevice(BloomFilter bloomFilter,
-            byte[] salt) {
-        return Data.FastPairDeviceWithAccountKey.newBuilder().build();
-    }
-
-    /**
      * Loads FastPair device accountKeys for a given account, but not other detailed fields.
      *
      * @throws IllegalStateException If ProxyFastPairDataProvider is not available.
diff --git a/nearby/service/java/com/android/server/nearby/util/DataUtils.java b/nearby/service/java/com/android/server/nearby/util/DataUtils.java
index 8bb83e9..c3bae08 100644
--- a/nearby/service/java/com/android/server/nearby/util/DataUtils.java
+++ b/nearby/service/java/com/android/server/nearby/util/DataUtils.java
@@ -57,11 +57,9 @@
      */
     public static String toString(ScanFastPairStoreItem item) {
         return "ScanFastPairStoreItem=[address:" + item.getAddress()
-                + ", actionUr:" + item.getActionUrl()
+                + ", actionUrl:" + item.getActionUrl()
                 + ", deviceName:" + item.getDeviceName()
-                + ", iconPng:" + item.getIconPng()
                 + ", iconFifeUrl:" + item.getIconFifeUrl()
-                + ", antiSpoofingKeyPair:" + item.getAntiSpoofingPublicKey()
                 + ", fastPairStrings:" + toString(item.getFastPairStrings())
                 + "]";
     }
diff --git a/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java b/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java
deleted file mode 100644
index 6021ff6..0000000
--- a/nearby/service/java/com/android/server/nearby/util/FastPairDecoder.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2022 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.server.nearby.util;
-
-import android.annotation.Nullable;
-import android.bluetooth.le.ScanRecord;
-import android.os.ParcelUuid;
-import android.util.SparseArray;
-
-import com.android.server.nearby.common.ble.BleFilter;
-import com.android.server.nearby.common.ble.BleRecord;
-
-import java.util.Arrays;
-
-/**
- * Parses Fast Pair information out of {@link BleRecord}s.
- *
- * <p>There are 2 different packet formats that are supported, which is used can be determined by
- * packet length:
- *
- * <p>For 3-byte packets, the full packet is the model ID.
- *
- * <p>For all other packets, the first byte is the header, followed by the model ID, followed by
- * zero or more extra fields. Each field has its own header byte followed by the field value. The
- * packet header is formatted as 0bVVVLLLLR (V = version, L = model ID length, R = reserved) and
- * each extra field header is 0bLLLLTTTT (L = field length, T = field type).
- */
-public class FastPairDecoder {
-
-    private static final int FIELD_TYPE_BLOOM_FILTER = 0;
-    private static final int FIELD_TYPE_BLOOM_FILTER_SALT = 1;
-    private static final int FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION = 2;
-    private static final int FIELD_TYPE_BATTERY = 3;
-    private static final int FIELD_TYPE_BATTERY_NO_NOTIFICATION = 4;
-    public static final int FIELD_TYPE_CONNECTION_STATE = 5;
-    private static final int FIELD_TYPE_RANDOM_RESOLVABLE_DATA = 6;
-
-
-    /** FE2C is the 16-bit Service UUID. The rest is the base UUID. See BluetoothUuid (hidden). */
-    private static final ParcelUuid FAST_PAIR_SERVICE_PARCEL_UUID =
-            ParcelUuid.fromString("0000FE2C-0000-1000-8000-00805F9B34FB");
-
-    /** The filter you use to scan for Fast Pair BLE advertisements. */
-    public static final BleFilter FILTER =
-            new BleFilter.Builder().setServiceData(FAST_PAIR_SERVICE_PARCEL_UUID,
-                    new byte[0]).build();
-
-    // NOTE: Ensure that all bitmasks are always ints, not bytes so that bitshifting works correctly
-    // without needing worry about signing errors.
-    private static final int HEADER_VERSION_BITMASK = 0b11100000;
-    private static final int HEADER_LENGTH_BITMASK = 0b00011110;
-    private static final int HEADER_VERSION_OFFSET = 5;
-    private static final int HEADER_LENGTH_OFFSET = 1;
-
-    private static final int EXTRA_FIELD_LENGTH_BITMASK = 0b11110000;
-    private static final int EXTRA_FIELD_TYPE_BITMASK = 0b00001111;
-    private static final int EXTRA_FIELD_LENGTH_OFFSET = 4;
-    private static final int EXTRA_FIELD_TYPE_OFFSET = 0;
-
-    private static final int MIN_ID_LENGTH = 3;
-    private static final int MAX_ID_LENGTH = 14;
-    private static final int HEADER_INDEX = 0;
-    private static final int HEADER_LENGTH = 1;
-    private static final int FIELD_HEADER_LENGTH = 1;
-
-    // Not using java.util.IllegalFormatException because it is unchecked.
-    private static class IllegalFormatException extends Exception {
-        private IllegalFormatException(String message) {
-            super(message);
-        }
-    }
-
-    /**
-     * Gets model id data from broadcast
-     */
-    @Nullable
-    public static byte[] getModelId(@Nullable byte[] serviceData) {
-        if (serviceData == null) {
-            return null;
-        }
-
-        if (serviceData.length >= MIN_ID_LENGTH) {
-            if (serviceData.length == MIN_ID_LENGTH) {
-                // If the length == 3, all bytes are the ID. See flag docs for more about
-                // endianness.
-                return serviceData;
-            } else {
-                // Otherwise, the first byte is a header which contains the length of the big-endian
-                // model ID that follows. The model ID will be trimmed if it contains leading zeros.
-                int idIndex = 1;
-                int end = idIndex + getIdLength(serviceData);
-                while (serviceData[idIndex] == 0 && end - idIndex > MIN_ID_LENGTH) {
-                    idIndex++;
-                }
-                return Arrays.copyOfRange(serviceData, idIndex, end);
-            }
-        }
-        return null;
-    }
-
-    /** Gets the FastPair service data array if available, otherwise returns null. */
-    @Nullable
-    public static byte[] getServiceDataArray(BleRecord bleRecord) {
-        return bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
-    }
-
-    /** Gets the FastPair service data array if available, otherwise returns null. */
-    @Nullable
-    public static byte[] getServiceDataArray(ScanRecord scanRecord) {
-        return scanRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
-    }
-
-    /** Gets the bloom filter from the extra fields if available, otherwise returns null. */
-    @Nullable
-    public static byte[] getBloomFilter(@Nullable byte[] serviceData) {
-        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER);
-    }
-
-    /** Gets the bloom filter salt from the extra fields if available, otherwise returns null. */
-    @Nullable
-    public static byte[] getBloomFilterSalt(byte[] serviceData) {
-        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_SALT);
-    }
-
-    /**
-     * Gets the suppress notification with bloom filter from the extra fields if available,
-     * otherwise returns null.
-     */
-    @Nullable
-    public static byte[] getBloomFilterNoNotification(@Nullable byte[] serviceData) {
-        return getExtraField(serviceData, FIELD_TYPE_BLOOM_FILTER_NO_NOTIFICATION);
-    }
-
-    /**
-     * Get random resolvableData
-     */
-    @Nullable
-    public static byte[] getRandomResolvableData(byte[] serviceData) {
-        return getExtraField(serviceData, FIELD_TYPE_RANDOM_RESOLVABLE_DATA);
-    }
-
-    @Nullable
-    private static byte[] getExtraField(@Nullable byte[] serviceData, int fieldId) {
-        if (serviceData == null || serviceData.length < HEADER_INDEX + HEADER_LENGTH) {
-            return null;
-        }
-        try {
-            return getExtraFields(serviceData).get(fieldId);
-        } catch (IllegalFormatException e) {
-            return null;
-        }
-    }
-
-    /** Gets extra field data at the end of the packet, defined by the extra field header. */
-    private static SparseArray<byte[]> getExtraFields(byte[] serviceData)
-            throws IllegalFormatException {
-        SparseArray<byte[]> extraFields = new SparseArray<>();
-        if (getVersion(serviceData) != 0) {
-            return extraFields;
-        }
-        int headerIndex = getFirstExtraFieldHeaderIndex(serviceData);
-        while (headerIndex < serviceData.length) {
-            int length = getExtraFieldLength(serviceData, headerIndex);
-            int index = headerIndex + FIELD_HEADER_LENGTH;
-            int type = getExtraFieldType(serviceData, headerIndex);
-            int end = index + length;
-            if (extraFields.get(type) == null) {
-                if (end <= serviceData.length) {
-                    extraFields.put(type, Arrays.copyOfRange(serviceData, index, end));
-                } else {
-                    throw new IllegalFormatException(
-                            "Invalid length, " + end + " is longer than service data size "
-                                    + serviceData.length);
-                }
-            }
-            headerIndex = end;
-        }
-        return extraFields;
-    }
-
-    /** Checks whether or not a valid ID is included in the service data packet. */
-    public static boolean hasBeaconIdBytes(BleRecord bleRecord) {
-        byte[] serviceData = bleRecord.getServiceData(FAST_PAIR_SERVICE_PARCEL_UUID);
-        return checkModelId(serviceData);
-    }
-
-    /** Check whether byte array is FastPair model id or not. */
-    public static boolean checkModelId(@Nullable byte[] scanResult) {
-        return scanResult != null
-                // The 3-byte format has no header byte (all bytes are the ID).
-                && (scanResult.length == MIN_ID_LENGTH
-                // Header byte exists. We support only format version 0. (A different version
-                // indicates
-                // a breaking change in the format.)
-                || (scanResult.length > MIN_ID_LENGTH
-                && getVersion(scanResult) == 0
-                && isIdLengthValid(scanResult)));
-    }
-
-    /** Checks whether or not bloom filter is included in the service data packet. */
-    public static boolean hasBloomFilter(BleRecord bleRecord) {
-        return (getBloomFilter(getServiceDataArray(bleRecord)) != null
-                || getBloomFilterNoNotification(getServiceDataArray(bleRecord)) != null);
-    }
-
-    /** Checks whether or not bloom filter is included in the service data packet. */
-    public static boolean hasBloomFilter(ScanRecord scanRecord) {
-        return (getBloomFilter(getServiceDataArray(scanRecord)) != null
-                || getBloomFilterNoNotification(getServiceDataArray(scanRecord)) != null);
-    }
-
-    private static int getVersion(byte[] serviceData) {
-        return serviceData.length == MIN_ID_LENGTH
-                ? 0
-                : (serviceData[HEADER_INDEX] & HEADER_VERSION_BITMASK) >> HEADER_VERSION_OFFSET;
-    }
-
-    private static int getIdLength(byte[] serviceData) {
-        return serviceData.length == MIN_ID_LENGTH
-                ? MIN_ID_LENGTH
-                : (serviceData[HEADER_INDEX] & HEADER_LENGTH_BITMASK) >> HEADER_LENGTH_OFFSET;
-    }
-
-    private static int getFirstExtraFieldHeaderIndex(byte[] serviceData) {
-        return HEADER_INDEX + HEADER_LENGTH + getIdLength(serviceData);
-    }
-
-    private static int getExtraFieldLength(byte[] serviceData, int extraFieldIndex) {
-        return (serviceData[extraFieldIndex] & EXTRA_FIELD_LENGTH_BITMASK)
-                >> EXTRA_FIELD_LENGTH_OFFSET;
-    }
-
-    private static int getExtraFieldType(byte[] serviceData, int extraFieldIndex) {
-        return (serviceData[extraFieldIndex] & EXTRA_FIELD_TYPE_BITMASK) >> EXTRA_FIELD_TYPE_OFFSET;
-    }
-
-    private static boolean isIdLengthValid(byte[] serviceData) {
-        int idLength = getIdLength(serviceData);
-        return MIN_ID_LENGTH <= idLength
-                && idLength <= MAX_ID_LENGTH
-                && idLength + HEADER_LENGTH <= serviceData.length;
-    }
-}
-
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java b/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java
new file mode 100644
index 0000000..3c5132d
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/Cryptor.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/** Class for encryption/decryption functionality. */
+public abstract class Cryptor {
+
+    /** AES only supports key sizes of 16, 24 or 32 bytes. */
+    static final int AUTHENTICITY_KEY_BYTE_SIZE = 16;
+
+    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
+
+    /**
+     * Encrypt the provided data blob.
+     *
+     * @param data data blob to be encrypted.
+     * @param salt used for IV
+     * @param secretKeyBytes secrete key accessed from credentials
+     * @return encrypted data, {@code null} if failed to encrypt.
+     */
+    @Nullable
+    public byte[] encrypt(byte[] data, byte[] salt, byte[] secretKeyBytes) {
+        return data;
+    }
+
+    /**
+     * Decrypt the original data blob from the provided byte array.
+     *
+     * @param encryptedData data blob to be decrypted.
+     * @param salt used for IV
+     * @param secretKeyBytes secrete key accessed from credentials
+     * @return decrypted data, {@code null} if failed to decrypt.
+     */
+    @Nullable
+    public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] secretKeyBytes) {
+        return encryptedData;
+    }
+
+    /**
+     * Generates a digital signature for the data.
+     *
+     * @return signature {@code null} if failed to sign
+     */
+    @Nullable
+    public byte[] sign(byte[] data, byte[] key) {
+        return new byte[0];
+    }
+
+    /**
+     * Verifies the signature generated by data and key, with the original signed data
+     */
+    public boolean verify(byte[] data, byte[] key, byte[] signature) {
+        return true;
+    }
+
+    /**
+     * @return length of the signature generated
+     */
+    public int getSignatureLength() {
+        return 0;
+    }
+
+    /**
+     * A HAMC sha256 based HKDF algorithm to pseudo randomly hash data and salt into a byte array of
+     * given size.
+     */
+    // Based on google3/third_party/tink/java/src/main/java/com/google/crypto/tink/subtle/Hkdf.java
+    @Nullable
+    static byte[] computeHkdf(byte[] ikm, byte[] salt, int size) {
+        Mac mac;
+        try {
+            mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            Log.w(TAG, "HMAC_SHA256_ALGORITHM is not supported.", e);
+            return null;
+        }
+
+        if (size > 255 * mac.getMacLength()) {
+            Log.w(TAG, "Size too large.");
+            return null;
+        }
+
+        if (salt.length == 0) {
+            Log.w(TAG, "Salt cannot be empty.");
+            return null;
+        }
+
+        try {
+            mac.init(new SecretKeySpec(salt, HMAC_SHA256_ALGORITHM));
+        } catch (InvalidKeyException e) {
+            Log.w(TAG, "Invalid key.", e);
+            return null;
+        }
+
+        byte[] prk = mac.doFinal(ikm);
+        byte[] result = new byte[size];
+        try {
+            mac.init(new SecretKeySpec(prk, HMAC_SHA256_ALGORITHM));
+        } catch (InvalidKeyException e) {
+            Log.w(TAG, "Invalid key.", e);
+            return null;
+        }
+
+        byte[] digest = new byte[0];
+        int ctr = 1;
+        int pos = 0;
+        while (true) {
+            mac.update(digest);
+            mac.update((byte) ctr);
+            digest = mac.doFinal();
+            if (pos + digest.length < size) {
+                System.arraycopy(digest, 0, result, pos, digest.length);
+                pos += digest.length;
+                ctr++;
+            } else {
+                System.arraycopy(digest, 0, result, pos, size - pos);
+                break;
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java
new file mode 100644
index 0000000..1c0ec9e
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpFake.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A Cryptor that returns the original data without actual encryption
+ */
+public class CryptorImpFake extends Cryptor {
+    // Lazily instantiated when {@link #getInstance()} is called.
+    @Nullable
+    private static CryptorImpFake sCryptor;
+
+    /** Returns an instance of CryptorImpFake. */
+    public static CryptorImpFake getInstance() {
+        if (sCryptor == null) {
+            sCryptor = new CryptorImpFake();
+        }
+        return sCryptor;
+    }
+
+    private CryptorImpFake() {
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java
new file mode 100644
index 0000000..b0e19b4
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpIdentityV1.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link android.nearby.BroadcastRequest#PRESENCE_VERSION_V1} for identity
+ * encryption and decryption.
+ */
+public class CryptorImpIdentityV1 extends Cryptor {
+
+    // 3 16 byte arrays known by both the encryptor and decryptor.
+    private static final byte[] EK_IV =
+            new byte[] {14, -123, -39, 42, 109, 127, 83, 27, 27, 11, 91, -38, 92, 17, -84, 66};
+    private static final byte[] ESALT_IV =
+            new byte[] {46, 83, -19, 10, -127, -31, -31, 12, 31, 76, 63, -9, 33, -66, 15, -10};
+    private static final byte[] KTAG_IV =
+            {-22, -83, -6, 67, 16, -99, -13, -9, 8, -3, -16, 37, -75, 47, 1, -56};
+
+    /** Length of encryption key required by AES/GCM encryption. */
+    private static final int ENCRYPTION_KEY_SIZE = 32;
+
+    /** Length of salt required by AES/GCM encryption. */
+    private static final int AES_CTR_IV_SIZE = 16;
+
+    /** Length HMAC tag */
+    public static final int HMAC_TAG_SIZE = 8;
+
+    /**
+     * In the form of "algorithm/mode/padding". Must be the same across broadcast and scan devices.
+     */
+    private static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
+
+    @VisibleForTesting
+    static final String ENCRYPT_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
+
+    // Lazily instantiated when {@link #getInstance()} is called.
+    @Nullable private static CryptorImpIdentityV1 sCryptor;
+
+    /** Returns an instance of CryptorImpIdentityV1. */
+    public static CryptorImpIdentityV1 getInstance() {
+        if (sCryptor == null) {
+            sCryptor = new CryptorImpIdentityV1();
+        }
+        return sCryptor;
+    }
+
+    @Nullable
+    @Override
+    public byte[] encrypt(byte[] data, byte[] salt, byte[] authenticityKey) {
+        if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
+            Log.w(TAG, "Illegal authenticity key size");
+            return null;
+        }
+
+        // Generates a 32 bytes encryption key from authenticity_key
+        byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, EK_IV, ENCRYPTION_KEY_SIZE);
+        if (encryptionKey == null) {
+            Log.e(TAG, "Failed to generate encryption key.");
+            return null;
+        }
+
+        // Encrypts the data using the encryption key
+        SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
+        Cipher cipher;
+        try {
+            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            Log.e(TAG, "Failed to encrypt with secret key.", e);
+            return null;
+        }
+        byte[] esalt = Cryptor.computeHkdf(salt, ESALT_IV, AES_CTR_IV_SIZE);
+        if (esalt == null) {
+            Log.e(TAG, "Failed to generate salt.");
+            return null;
+        }
+        try {
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(esalt));
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            Log.e(TAG, "Failed to initialize cipher.", e);
+            return null;
+        }
+        try {
+            return cipher.doFinal(data);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            Log.e(TAG, "Failed to encrypt with secret key.", e);
+            return null;
+        }
+    }
+
+    @Nullable
+    @Override
+    public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] authenticityKey) {
+        if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
+            Log.w(TAG, "Illegal authenticity key size");
+            return null;
+        }
+
+        // Generates a 32 bytes encryption key from authenticity_key
+        byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, EK_IV, ENCRYPTION_KEY_SIZE);
+        if (encryptionKey == null) {
+            Log.e(TAG, "Failed to generate encryption key.");
+            return null;
+        }
+
+        // Decrypts the data using the encryption key
+        SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
+        Cipher cipher;
+        try {
+            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            Log.e(TAG, "Failed to get cipher instance.", e);
+            return null;
+        }
+        byte[] esalt = Cryptor.computeHkdf(salt, ESALT_IV, AES_CTR_IV_SIZE);
+        if (esalt == null) {
+            return null;
+        }
+        try {
+            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(esalt));
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            Log.e(TAG, "Failed to initialize cipher.", e);
+            return null;
+        }
+
+        try {
+            return cipher.doFinal(encryptedData);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            Log.e(TAG, "Failed to decrypt bytes with secret key.", e);
+            return null;
+        }
+    }
+
+    /**
+     * Generates a digital signature for the data.
+     *
+     * @return signature {@code null} if failed to sign
+     */
+    @Nullable
+    @Override
+    public byte[] sign(byte[] data, byte[] salt) {
+        if (data == null) {
+            Log.e(TAG, "Not generate HMAC tag because of invalid data input.");
+            return null;
+        }
+
+        // Generates a 8 bytes HMAC tag
+        return Cryptor.computeHkdf(data, salt, HMAC_TAG_SIZE);
+    }
+
+    /**
+     * Generates a digital signature for the data.
+     * Uses KTAG_IV as salt value.
+     */
+    @Nullable
+    public byte[] sign(byte[] data) {
+        // Generates a 8 bytes HMAC tag
+        return sign(data, KTAG_IV);
+    }
+
+    @Override
+    public boolean verify(byte[] data, byte[] key, byte[] signature) {
+        return Arrays.equals(sign(data, key), signature);
+    }
+
+    /**
+     * Verifies the signature generated by data and key, with the original signed data. Uses
+     * KTAG_IV as salt value.
+     */
+    public boolean verify(byte[] data, byte[] signature) {
+        return verify(data, KTAG_IV, signature);
+    }
+}
diff --git a/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java
new file mode 100644
index 0000000..15073fb
--- /dev/null
+++ b/nearby/service/java/com/android/server/nearby/util/encryption/CryptorImpV1.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.android.server.nearby.NearbyService.TAG;
+
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link android.nearby.BroadcastRequest#PRESENCE_VERSION_V1} for encryption and decryption.
+ */
+public class CryptorImpV1 extends Cryptor {
+
+    /**
+     * In the form of "algorithm/mode/padding". Must be the same across broadcast and scan devices.
+     */
+    private static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
+
+    @VisibleForTesting
+    static final String ENCRYPT_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
+
+    /** Length of encryption key required by AES/GCM encryption. */
+    private static final int ENCRYPTION_KEY_SIZE = 32;
+
+    /** Length of salt required by AES/GCM encryption. */
+    private static final int AES_CTR_IV_SIZE = 16;
+
+    /** Length HMAC tag */
+    public static final int HMAC_TAG_SIZE = 16;
+
+    // 3 16 byte arrays known by both the encryptor and decryptor.
+    private static final byte[] AK_IV =
+            new byte[] {12, -59, 19, 23, 96, 57, -59, 19, 117, -31, -116, -61, 86, -25, -33, -78};
+    private static final byte[] ASALT_IV =
+            new byte[] {111, 48, -83, -79, -10, -102, -16, 73, 43, 55, 102, -127, 58, -19, -113, 4};
+    private static final byte[] HK_IV =
+            new byte[] {12, -59, 19, 23, 96, 57, -59, 19, 117, -31, -116, -61, 86, -25, -33, -78};
+
+    // Lazily instantiated when {@link #getInstance()} is called.
+    @Nullable private static CryptorImpV1 sCryptor;
+
+    /** Returns an instance of CryptorImpV1. */
+    public static CryptorImpV1 getInstance() {
+        if (sCryptor == null) {
+            sCryptor = new CryptorImpV1();
+        }
+        return sCryptor;
+    }
+
+    private CryptorImpV1() {
+    }
+
+    @Nullable
+    @Override
+    public byte[] encrypt(byte[] data, byte[] salt, byte[] authenticityKey) {
+        if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
+            Log.w(TAG, "Illegal authenticity key size");
+            return null;
+        }
+
+        // Generates a 32 bytes encryption key from authenticity_key
+        byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, AK_IV, ENCRYPTION_KEY_SIZE);
+        if (encryptionKey == null) {
+            Log.e(TAG, "Failed to generate encryption key.");
+            return null;
+        }
+
+        // Encrypts the data using the encryption key
+        SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
+        Cipher cipher;
+        try {
+            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            Log.e(TAG, "Failed to encrypt with secret key.", e);
+            return null;
+        }
+        byte[] asalt = Cryptor.computeHkdf(salt, ASALT_IV, AES_CTR_IV_SIZE);
+        if (asalt == null) {
+            Log.e(TAG, "Failed to generate salt.");
+            return null;
+        }
+        try {
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(asalt));
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            Log.e(TAG, "Failed to initialize cipher.", e);
+            return null;
+        }
+        try {
+            return cipher.doFinal(data);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            Log.e(TAG, "Failed to encrypt with secret key.", e);
+            return null;
+        }
+    }
+
+    @Nullable
+    @Override
+    public byte[] decrypt(byte[] encryptedData, byte[] salt, byte[] authenticityKey) {
+        if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
+            Log.w(TAG, "Illegal authenticity key size");
+            return null;
+        }
+
+        // Generates a 32 bytes encryption key from authenticity_key
+        byte[] encryptionKey = Cryptor.computeHkdf(authenticityKey, AK_IV, ENCRYPTION_KEY_SIZE);
+        if (encryptionKey == null) {
+            Log.e(TAG, "Failed to generate encryption key.");
+            return null;
+        }
+
+        // Decrypts the data using the encryption key
+        SecretKey secretKey = new SecretKeySpec(encryptionKey, ENCRYPT_ALGORITHM);
+        Cipher cipher;
+        try {
+            cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+            Log.e(TAG, "Failed to get cipher instance.", e);
+            return null;
+        }
+        byte[] asalt = Cryptor.computeHkdf(salt, ASALT_IV, AES_CTR_IV_SIZE);
+        if (asalt == null) {
+            return null;
+        }
+        try {
+            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(asalt));
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            Log.e(TAG, "Failed to initialize cipher.", e);
+            return null;
+        }
+
+        try {
+            return cipher.doFinal(encryptedData);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            Log.e(TAG, "Failed to decrypt bytes with secret key.", e);
+            return null;
+        }
+    }
+
+    @Override
+    @Nullable
+    public byte[] sign(byte[] data, byte[] key) {
+        return generateHmacTag(data, key);
+    }
+
+    @Override
+    public int getSignatureLength() {
+        return HMAC_TAG_SIZE;
+    }
+
+    @Override
+    public boolean verify(byte[] data, byte[] key, byte[] signature) {
+        return Arrays.equals(sign(data, key), signature);
+    }
+
+    /** Generates a 16 bytes HMAC tag. This is used for decryptor to verify if the computed HMAC tag
+     * is equal to HMAC tag in advertisement to see data integrity. */
+    @Nullable
+    @VisibleForTesting
+    byte[] generateHmacTag(byte[] data, byte[] authenticityKey) {
+        if (data == null || authenticityKey == null) {
+            Log.e(TAG, "Not generate HMAC tag because of invalid data input.");
+            return null;
+        }
+
+        if (authenticityKey.length != AUTHENTICITY_KEY_BYTE_SIZE) {
+            Log.e(TAG, "Illegal authenticity key size");
+            return null;
+        }
+
+        // Generates a 32 bytes HMAC key from authenticity_key
+        byte[] hmacKey = Cryptor.computeHkdf(authenticityKey, HK_IV, AES_CTR_IV_SIZE);
+        if (hmacKey == null) {
+            Log.e(TAG, "Failed to generate HMAC key.");
+            return null;
+        }
+
+        // Generates a 16 bytes HMAC tag from authenticity_key
+        return Cryptor.computeHkdf(data, hmacKey, HMAC_TAG_SIZE);
+    }
+}
diff --git a/nearby/service/proto/src/presence/blefilter.proto b/nearby/service/proto/src/presence/blefilter.proto
index 9f75d34..6e1ba6d 100644
--- a/nearby/service/proto/src/presence/blefilter.proto
+++ b/nearby/service/proto/src/presence/blefilter.proto
@@ -47,6 +47,7 @@
   optional bytes metadata_encryption_key_tag = 2;
 }
 
+// Public credential returned in BleFilterResult.
 message PublicCredential {
   optional bytes secret_id = 1;
   optional bytes authenticity_key = 2;
@@ -55,6 +56,20 @@
   optional bytes encrypted_metadata_tag = 5;
 }
 
+message DataElement {
+  enum ElementType {
+    DE_NONE = 0;
+    DE_FAST_PAIR_ACCOUNT_KEY = 9;
+    DE_CONNECTION_STATUS = 10;
+    DE_BATTERY_STATUS = 11;
+  }
+
+  optional ElementType key = 1;
+  optional bytes value = 2;
+  optional uint32 value_length = 3;
+}
+
+// A single filter used to filter BLE events.
 message BleFilter {
   optional uint32 id = 1;  // Required, unique id of this filter.
   // Maximum delay to notify the client after an event occurs.
@@ -71,7 +86,9 @@
   // the period of latency defined above.
   optional float distance_m = 7;
   // Used to verify the list of trusted devices.
-  repeated PublicateCertificate certficate = 8;
+  repeated PublicateCertificate certificate = 8;
+  // Data Elements for extended properties.
+  repeated DataElement data_element = 9;
 }
 
 message BleFilters {
@@ -80,14 +97,33 @@
 
 // FilterResult is returned to host when a BLE event matches a Filter.
 message BleFilterResult {
+  enum ResultType {
+    RESULT_NONE = 0;
+    RESULT_PRESENCE = 1;
+    RESULT_FAST_PAIR = 2;
+  }
+
   optional uint32 id = 1;  // id of the matched Filter.
-  optional uint32 tx_power = 2;
-  optional uint32 rssi = 3;
+  optional int32 tx_power = 2;
+  optional int32 rssi = 3;
   optional uint32 intent = 4;
   optional bytes bluetooth_address = 5;
   optional PublicCredential public_credential = 6;
+  repeated DataElement data_element = 7;
+  optional bytes ble_service_data = 8;
+  optional ResultType result_type = 9;
 }
 
 message BleFilterResults {
   repeated BleFilterResult result = 1;
 }
+
+message BleConfig {
+  // True to start BLE scan. Otherwise, stop BLE scan.
+  optional bool start_scan = 1;
+  // True when screen is turned on. Otherwise, set to false when screen is
+  // turned off.
+  optional bool screen_on = 2;
+  // Fast Pair cache expires after this time period.
+  optional uint64 fast_pair_cache_expire_time_sec = 3;
+}
diff --git a/nearby/tests/cts/fastpair/Android.bp b/nearby/tests/cts/fastpair/Android.bp
index 845ed84..0410cd5 100644
--- a/nearby/tests/cts/fastpair/Android.bp
+++ b/nearby/tests/cts/fastpair/Android.bp
@@ -31,6 +31,7 @@
     libs: [
         "android.test.base",
         "framework-bluetooth.stubs.module_lib",
+        "framework-configinfrastructure",
         "framework-connectivity-t.impl",
     ],
     srcs: ["src/**/*.java"],
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/CredentialElementTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/CredentialElementTest.java
index aacb6d8..a2da967 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/CredentialElementTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/CredentialElementTest.java
@@ -42,7 +42,6 @@
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void testBuilder() {
         CredentialElement element = new CredentialElement(KEY, VALUE);
-
         assertThat(element.getKey()).isEqualTo(KEY);
         assertThat(Arrays.equals(element.getValue(), VALUE)).isTrue();
     }
@@ -58,9 +57,31 @@
         CredentialElement elementFromParcel = element.CREATOR.createFromParcel(
                 parcel);
         parcel.recycle();
-
         assertThat(elementFromParcel.getKey()).isEqualTo(KEY);
         assertThat(Arrays.equals(elementFromParcel.getValue(), VALUE)).isTrue();
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        CredentialElement element = new CredentialElement(KEY, VALUE);
+        assertThat(element.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEqual() {
+        CredentialElement element1 = new CredentialElement(KEY, VALUE);
+        CredentialElement element2 = new CredentialElement(KEY, VALUE);
+        assertThat(element1.equals(element2)).isTrue();
+        assertThat(element1.hashCode()).isEqualTo(element2.hashCode());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        CredentialElement [] elements =
+                CredentialElement.CREATOR.newArray(2);
+        assertThat(elements.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
index ec6e89a..84814ae 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/DataElementTest.java
@@ -16,6 +16,13 @@
 
 package android.nearby.cts;
 
+import static android.nearby.DataElement.DataType.PRIVATE_IDENTITY;
+import static android.nearby.DataElement.DataType.PROVISIONED_IDENTITY;
+import static android.nearby.DataElement.DataType.PUBLIC_IDENTITY;
+import static android.nearby.DataElement.DataType.SALT;
+import static android.nearby.DataElement.DataType.TRUSTED_IDENTITY;
+import static android.nearby.DataElement.DataType.TX_POWER;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.nearby.DataElement;
@@ -31,12 +38,11 @@
 
 import java.util.Arrays;
 
-
 @RunWith(AndroidJUnit4.class)
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class DataElementTest {
 
-    private static final int KEY = 1234;
+    private static final int KEY = 1;
     private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
 
     @Test
@@ -63,4 +69,59 @@
         assertThat(elementFromParcel.getKey()).isEqualTo(KEY);
         assertThat(Arrays.equals(elementFromParcel.getValue(), VALUE)).isTrue();
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        DataElement dataElement = new DataElement(KEY, VALUE);
+        assertThat(dataElement.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        DataElement[] elements =
+                DataElement.CREATOR.newArray(2);
+        assertThat(elements.length).isEqualTo(2);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEquals() {
+        DataElement dataElement = new DataElement(KEY, VALUE);
+        DataElement dataElement2 = new DataElement(KEY, VALUE);
+
+        assertThat(dataElement.equals(dataElement2)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testIsIdentity() {
+        DataElement privateIdentity = new DataElement(PRIVATE_IDENTITY, new byte[]{1, 2, 3});
+        DataElement trustedIdentity = new DataElement(TRUSTED_IDENTITY, new byte[]{1, 2, 3});
+        DataElement publicIdentity = new DataElement(PUBLIC_IDENTITY, new byte[]{1, 2, 3});
+        DataElement provisionedIdentity =
+                new DataElement(PROVISIONED_IDENTITY, new byte[]{1, 2, 3});
+
+        DataElement salt = new DataElement(SALT, new byte[]{1, 2, 3});
+        DataElement txPower = new DataElement(TX_POWER, new byte[]{1, 2, 3});
+
+        assertThat(privateIdentity.isIdentityDataType()).isTrue();
+        assertThat(trustedIdentity.isIdentityDataType()).isTrue();
+        assertThat(publicIdentity.isIdentityDataType()).isTrue();
+        assertThat(provisionedIdentity.isIdentityDataType()).isTrue();
+        assertThat(salt.isIdentityDataType()).isFalse();
+        assertThat(txPower.isIdentityDataType()).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_notEquals() {
+        DataElement dataElement = new DataElement(KEY, VALUE);
+        DataElement dataElement2 = new DataElement(KEY, new byte[]{1, 2, 1, 1});
+        DataElement dataElement3 = new DataElement(6, VALUE);
+
+        assertThat(dataElement.equals(dataElement2)).isFalse();
+        assertThat(dataElement.equals(dataElement3)).isFalse();
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
index dd9cbb0..2907e56 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
@@ -42,8 +42,11 @@
 
     private static final String BLUETOOTH_ADDRESS = "00:11:22:33:FF:EE";
     private static final byte[] SCAN_DATA = new byte[] {1, 2, 3, 4};
+    private static final byte[] SALT = new byte[] {1, 2, 3, 4};
     private static final String FAST_PAIR_MODEL_ID = "1234";
     private static final int RSSI = -60;
+    private static final int TX_POWER = -10;
+    private static final int ACTION = 1;
 
     private NearbyDeviceParcelable.Builder mBuilder;
 
@@ -60,48 +63,38 @@
                         .setData(SCAN_DATA);
     }
 
-    /** Verify toString returns expected string. */
     @Test
     @SdkSuppress(minSdkVersion = 33, codeName = "T")
-    public void testToString() {
+    public void testNullFields() {
         PublicCredential publicCredential =
                 new PublicCredential.Builder(
-                                new byte[] {1},
-                                new byte[] {2},
-                                new byte[] {3},
-                                new byte[] {4},
-                                new byte[] {5})
+                        new byte[] {1},
+                        new byte[] {2},
+                        new byte[] {3},
+                        new byte[] {4},
+                        new byte[] {5})
                         .build();
         NearbyDeviceParcelable nearbyDeviceParcelable =
-                mBuilder.setFastPairModelId(null)
-                        .setData(null)
-                        .setPublicCredential(publicCredential)
-                        .build();
-
-        assertThat(nearbyDeviceParcelable.toString())
-                .isEqualTo(
-                        "NearbyDeviceParcelable[scanType=2, name=testDevice, medium=BLE, "
-                                + "txPower=0, rssi=-60, action=0, bluetoothAddress="
-                                + BLUETOOTH_ADDRESS
-                                + ", fastPairModelId=null, data=null, salt=null]");
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 33, codeName = "T")
-    public void test_defaultNullFields() {
-        NearbyDeviceParcelable nearbyDeviceParcelable =
                 new NearbyDeviceParcelable.Builder()
                         .setMedium(NearbyDevice.Medium.BLE)
+                        .setPublicCredential(publicCredential)
+                        .setAction(ACTION)
                         .setRssi(RSSI)
+                        .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                        .setTxPower(TX_POWER)
+                        .setSalt(SALT)
                         .build();
 
         assertThat(nearbyDeviceParcelable.getName()).isNull();
         assertThat(nearbyDeviceParcelable.getFastPairModelId()).isNull();
         assertThat(nearbyDeviceParcelable.getBluetoothAddress()).isNull();
         assertThat(nearbyDeviceParcelable.getData()).isNull();
-
         assertThat(nearbyDeviceParcelable.getMedium()).isEqualTo(NearbyDevice.Medium.BLE);
         assertThat(nearbyDeviceParcelable.getRssi()).isEqualTo(RSSI);
+        assertThat(nearbyDeviceParcelable.getAction()).isEqualTo(ACTION);
+        assertThat(nearbyDeviceParcelable.getPublicCredential()).isEqualTo(publicCredential);
+        assertThat(nearbyDeviceParcelable.getSalt()).isEqualTo(SALT);
+        assertThat(nearbyDeviceParcelable.getTxPower()).isEqualTo(TX_POWER);
     }
 
     @Test
@@ -141,7 +134,6 @@
     @SdkSuppress(minSdkVersion = 33, codeName = "T")
     public void testWriteParcel_nullBluetoothAddress() {
         NearbyDeviceParcelable nearbyDeviceParcelable = mBuilder.setBluetoothAddress(null).build();
-
         Parcel parcel = Parcel.obtain();
         nearbyDeviceParcelable.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
@@ -151,4 +143,36 @@
 
         assertThat(actualNearbyDevice.getBluetoothAddress()).isNull();
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
+    public void describeContents() {
+        NearbyDeviceParcelable nearbyDeviceParcelable = mBuilder.setBluetoothAddress(null).build();
+        assertThat(nearbyDeviceParcelable.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
+    public void testEqual() {
+        PublicCredential publicCredential =
+                new PublicCredential.Builder(
+                        new byte[] {1},
+                        new byte[] {2},
+                        new byte[] {3},
+                        new byte[] {4},
+                        new byte[] {5})
+                        .build();
+        NearbyDeviceParcelable nearbyDeviceParcelable1 =
+                mBuilder.setPublicCredential(publicCredential).build();
+        NearbyDeviceParcelable nearbyDeviceParcelable2 =
+                mBuilder.setPublicCredential(publicCredential).build();
+        assertThat(nearbyDeviceParcelable1.equals(nearbyDeviceParcelable2)).isTrue();
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        NearbyDeviceParcelable[] nearbyDeviceParcelables =
+                NearbyDeviceParcelable.CREATOR.newArray(2);
+        assertThat(nearbyDeviceParcelables.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceTest.java
index f37800a..8ca5a94 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceTest.java
@@ -16,6 +16,8 @@
 
 package android.nearby.cts;
 
+import static android.nearby.NearbyDevice.Medium.BLE;
+
 import android.annotation.TargetApi;
 import android.nearby.FastPairDevice;
 import android.nearby.NearbyDevice;
@@ -34,13 +36,18 @@
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 @TargetApi(Build.VERSION_CODES.TIRAMISU)
 public class NearbyDeviceTest {
+    private static final String NAME = "NearbyDevice";
+    private static final String MODEL_ID = "112233";
+    private static final int TX_POWER = -10;
+    private static final int RSSI = -60;
+    private static final String BLUETOOTH_ADDRESS = "00:11:22:33:FF:EE";
+    private static final byte[] SCAN_DATA = new byte[] {1, 2, 3, 4};
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void test_isValidMedium() {
         assertThat(NearbyDevice.isValidMedium(1)).isTrue();
         assertThat(NearbyDevice.isValidMedium(2)).isTrue();
-
         assertThat(NearbyDevice.isValidMedium(0)).isFalse();
         assertThat(NearbyDevice.isValidMedium(3)).isFalse();
     }
@@ -49,11 +56,55 @@
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void test_getMedium_fromChild() {
         FastPairDevice fastPairDevice = new FastPairDevice.Builder()
-                .addMedium(NearbyDevice.Medium.BLE)
-                .setRssi(-60)
+                .addMedium(BLE)
+                .setRssi(RSSI)
                 .build();
 
         assertThat(fastPairDevice.getMediums()).contains(1);
-        assertThat(fastPairDevice.getRssi()).isEqualTo(-60);
+        assertThat(fastPairDevice.getRssi()).isEqualTo(RSSI);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEqual() {
+        FastPairDevice fastPairDevice1 = new FastPairDevice.Builder()
+                .setModelId(MODEL_ID)
+                .setTxPower(TX_POWER)
+                .setBluetoothAddress(BLUETOOTH_ADDRESS)
+                .setData(SCAN_DATA)
+                .setRssi(RSSI)
+                .addMedium(BLE)
+                .setName(NAME)
+                .build();
+        FastPairDevice fastPairDevice2 = new FastPairDevice.Builder()
+                .setModelId(MODEL_ID)
+                .setTxPower(TX_POWER)
+                .setBluetoothAddress(BLUETOOTH_ADDRESS)
+                .setData(SCAN_DATA)
+                .setRssi(RSSI)
+                .addMedium(BLE)
+                .setName(NAME)
+                .build();
+
+        assertThat(fastPairDevice1.equals(fastPairDevice1)).isTrue();
+        assertThat(fastPairDevice1.equals(fastPairDevice2)).isTrue();
+        assertThat(fastPairDevice1.equals(null)).isFalse();
+        assertThat(fastPairDevice1.hashCode()).isEqualTo(fastPairDevice2.hashCode());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testToString() {
+        FastPairDevice fastPairDevice1 = new FastPairDevice.Builder()
+                .addMedium(BLE)
+                .setRssi(RSSI)
+                .setModelId(MODEL_ID)
+                .setTxPower(TX_POWER)
+                .setBluetoothAddress(BLUETOOTH_ADDRESS)
+                .build();
+
+        assertThat(fastPairDevice1.toString())
+                .isEqualTo("FastPairDevice [medium={BLE} rssi=-60 "
+                        + "txPower=-10 modelId=112233 bluetoothAddress=00:11:22:33:FF:EE]");
     }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
index 7696a61..462d05a 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -158,6 +158,15 @@
         mNearbyManager.stopBroadcast(callback);
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void setFastPairScanEnabled() {
+        mNearbyManager.setFastPairScanEnabled(mContext, true);
+        assertThat(mNearbyManager.getFastPairScanEnabled(mContext)).isTrue();
+        mNearbyManager.setFastPairScanEnabled(mContext, false);
+        assertThat(mNearbyManager.getFastPairScanEnabled(mContext)).isFalse();
+    }
+
     private void enableBluetooth() {
         BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
         BluetoothAdapter bluetoothAdapter = manager.getAdapter();
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
index 1daa410..71be889 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceBroadcastRequestTest.java
@@ -54,7 +54,7 @@
     private static final byte[] SECRETE_ID = new byte[]{1, 2, 3, 4};
     private static final byte[] AUTHENTICITY_KEY = new byte[]{0, 1, 1, 1};
     private static final byte[] METADATA_ENCRYPTION_KEY = new byte[]{1, 1, 3, 4, 5};
-    private static final int KEY = 1234;
+    private static final int KEY = 3;
     private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
     private static final String DEVICE_NAME = "test_device";
 
@@ -114,4 +114,18 @@
         assertThat(parcelRequest.getType()).isEqualTo(BROADCAST_TYPE_NEARBY_PRESENCE);
 
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        PresenceBroadcastRequest broadcastRequest = mBuilder.build();
+        assertThat(broadcastRequest.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        PresenceBroadcastRequest[] presenceBroadcastRequests =
+                PresenceBroadcastRequest.CREATOR.newArray(2);
+        assertThat(presenceBroadcastRequests.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
index 5fefc68..ea1de6b 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceDeviceTest.java
@@ -45,7 +45,7 @@
     private static final int RSSI = -40;
     private static final int MEDIUM = NearbyDevice.Medium.BLE;
     private static final String DEVICE_NAME = "testDevice";
-    private static final int KEY = 1234;
+    private static final int KEY = 3;
     private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
     private static final byte[] SALT = new byte[]{2, 3};
     private static final byte[] SECRET_ID = new byte[]{11, 13};
@@ -104,4 +104,24 @@
         assertThat(parcelDevice.getMediums()).containsExactly(MEDIUM);
         assertThat(parcelDevice.getName()).isEqualTo(DEVICE_NAME);
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        PresenceDevice device =
+                new PresenceDevice.Builder(DEVICE_ID, SALT, SECRET_ID, ENCRYPTED_IDENTITY)
+                        .addExtendedProperty(new DataElement(KEY, VALUE))
+                        .setRssi(RSSI)
+                        .addMedium(MEDIUM)
+                        .setName(DEVICE_NAME)
+                        .build();
+        assertThat(device.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        PresenceDevice[] devices =
+                PresenceDevice.CREATOR.newArray(2);
+        assertThat(devices.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java
index b7fe40a..77e7dc5 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PresenceScanFilterTest.java
@@ -48,7 +48,7 @@
     private static final byte[] PUBLIC_KEY = new byte[]{1, 1, 2, 2};
     private static final byte[] ENCRYPTED_METADATA = new byte[]{1, 2, 3, 4, 5};
     private static final byte[] METADATA_ENCRYPTION_KEY_TAG = new byte[]{1, 1, 3, 4, 5};
-    private static final int KEY = 1234;
+    private static final int KEY = 3;
     private static final byte[] VALUE = new byte[]{1, 1, 1, 1};
 
 
@@ -90,5 +90,21 @@
         assertThat(parcelFilter.getType()).isEqualTo(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE);
         assertThat(parcelFilter.getMaxPathLoss()).isEqualTo(RSSI);
         assertThat(parcelFilter.getPresenceActions()).containsExactly(ACTION);
+        assertThat(parcelFilter.getExtendedProperties().get(0).getKey()).isEqualTo(KEY);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        PresenceScanFilter filter = mBuilder.build();
+        assertThat(filter.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        PresenceScanFilter[] filters =
+                PresenceScanFilter.CREATOR.newArray(2);
+        assertThat(filters.length).isEqualTo(2);
     }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
index f05f65f..fa8c954 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PrivateCredentialTest.java
@@ -99,4 +99,19 @@
         assertThat(credentialElement.getKey()).isEqualTo(KEY);
         assertThat(Arrays.equals(credentialElement.getValue(), VALUE)).isTrue();
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
+    public void describeContents() {
+        PrivateCredential credential = mBuilder.build();
+        assertThat(credential.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
+    public void testCreatorNewArray() {
+        PrivateCredential[]  credentials =
+                PrivateCredential.CREATOR.newArray(2);
+        assertThat(credentials.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
index 11bbacc..774e897 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/PublicCredentialTest.java
@@ -135,6 +135,7 @@
                         .setIdentityType(IDENTITY_TYPE_PRIVATE)
                         .build();
         assertThat(credentialOne.equals((Object) credentialTwo)).isTrue();
+        assertThat(credentialOne.equals(null)).isFalse();
     }
 
     @Test
@@ -161,4 +162,19 @@
                         .build();
         assertThat(credentialOne.equals((Object) credentialTwo)).isFalse();
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        PublicCredential credential = mBuilder.build();
+        assertThat(credential.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        PublicCredential[] credentials  =
+        PublicCredential.CREATOR.newArray(2);
+        assertThat(credentials.length).isEqualTo(2);
+    }
 }
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
index 21f3d28..de4b1c3 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/ScanRequestTest.java
@@ -171,6 +171,23 @@
         assertThat(request.getScanFilters().get(0).getMaxPathLoss()).isEqualTo(RSSI);
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        ScanRequest request = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_FAST_PAIR)
+                .build();
+        assertThat(request.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        ScanRequest[] requests =
+                ScanRequest.CREATOR.newArray(2);
+        assertThat(requests.length).isEqualTo(2);
+    }
+
     private static PresenceScanFilter getPresenceScanFilter() {
         final byte[] secretId = new byte[]{1, 2, 3, 4};
         final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp
index 328751a..b406776 100644
--- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp
+++ b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/Android.bp
@@ -23,8 +23,8 @@
     static_libs: [
         // TODO(b/228406038): Remove "framework-nearby-static" once Fast Pair system APIs add back.
         "framework-nearby-static",
+        "gson",
         "guava",
-        "gson-prebuilt-jar",
     ],
 }
 
diff --git a/nearby/tests/unit/AndroidManifest.xml b/nearby/tests/unit/AndroidManifest.xml
index 9f58baf..7dcb263 100644
--- a/nearby/tests/unit/AndroidManifest.xml
+++ b/nearby/tests/unit/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/nearby/tests/unit/src/android/nearby/FastPairAntispoofKeyDeviceMetadataTest.java b/nearby/tests/unit/src/android/nearby/FastPairAntispoofKeyDeviceMetadataTest.java
new file mode 100644
index 0000000..d095529
--- /dev/null
+++ b/nearby/tests/unit/src/android/nearby/FastPairAntispoofKeyDeviceMetadataTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 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 android.nearby;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class FastPairAntispoofKeyDeviceMetadataTest {
+
+    private static final int BLE_TX_POWER  = 5;
+    private static final String CONNECT_SUCCESS_COMPANION_APP_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_INSTALLED";
+    private static final String CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED";
+    private static final float DELTA = 0.001f;
+    private static final int DEVICE_TYPE = 7;
+    private static final String DOWNLOAD_COMPANION_APP_DESCRIPTION =
+            "DOWNLOAD_COMPANION_APP_DESCRIPTION";
+    private static final String FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION =
+            "FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION";
+    private static final byte[] IMAGE = new byte[] {7, 9};
+    private static final String IMAGE_URL = "IMAGE_URL";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION =
+            "INITIAL_NOTIFICATION_DESCRIPTION";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT =
+            "INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT";
+    private static final String INITIAL_PAIRING_DESCRIPTION = "INITIAL_PAIRING_DESCRIPTION";
+    private static final String INTENT_URI = "INTENT_URI";
+    private static final String OPEN_COMPANION_APP_DESCRIPTION = "OPEN_COMPANION_APP_DESCRIPTION";
+    private static final String RETRO_ACTIVE_PAIRING_DESCRIPTION =
+            "RETRO_ACTIVE_PAIRING_DESCRIPTION";
+    private static final String SUBSEQUENT_PAIRING_DESCRIPTION = "SUBSEQUENT_PAIRING_DESCRIPTION";
+    private static final float TRIGGER_DISTANCE = 111;
+    private static final String TRUE_WIRELESS_IMAGE_URL_CASE = "TRUE_WIRELESS_IMAGE_URL_CASE";
+    private static final String TRUE_WIRELESS_IMAGE_URL_LEFT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_LEFT_BUD";
+    private static final String TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD";
+    private static final String UNABLE_TO_CONNECT_DESCRIPTION = "UNABLE_TO_CONNECT_DESCRIPTION";
+    private static final String UNABLE_TO_CONNECT_TITLE = "UNABLE_TO_CONNECT_TITLE";
+    private static final String UPDATE_COMPANION_APP_DESCRIPTION =
+            "UPDATE_COMPANION_APP_DESCRIPTION";
+    private static final String WAIT_LAUNCH_COMPANION_APP_DESCRIPTION =
+            "WAIT_LAUNCH_COMPANION_APP_DESCRIPTION";
+    private static final byte[] ANTI_SPOOFING_KEY = new byte[] {4, 5, 6};
+    private static final String NAME = "NAME";
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetGetFastPairAntispoofKeyDeviceMetadataNotNull() {
+        FastPairDeviceMetadata fastPairDeviceMetadata = genFastPairDeviceMetadata();
+        FastPairAntispoofKeyDeviceMetadata fastPairAntispoofKeyDeviceMetadata =
+                genFastPairAntispoofKeyDeviceMetadata(ANTI_SPOOFING_KEY, fastPairDeviceMetadata);
+
+        assertThat(fastPairAntispoofKeyDeviceMetadata.getAntispoofPublicKey()).isEqualTo(
+                ANTI_SPOOFING_KEY);
+        ensureFastPairDeviceMetadataAsExpected(
+                fastPairAntispoofKeyDeviceMetadata.getFastPairDeviceMetadata());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetGetFastPairAntispoofKeyDeviceMetadataNull() {
+        FastPairAntispoofKeyDeviceMetadata fastPairAntispoofKeyDeviceMetadata =
+                genFastPairAntispoofKeyDeviceMetadata(null, null);
+        assertThat(fastPairAntispoofKeyDeviceMetadata.getAntispoofPublicKey()).isEqualTo(
+                null);
+        assertThat(fastPairAntispoofKeyDeviceMetadata.getFastPairDeviceMetadata()).isEqualTo(
+                null);
+    }
+
+    /* Verifies DeviceMetadata. */
+    private static void ensureFastPairDeviceMetadataAsExpected(FastPairDeviceMetadata metadata) {
+        assertThat(metadata.getBleTxPower()).isEqualTo(BLE_TX_POWER);
+        assertThat(metadata.getConnectSuccessCompanionAppInstalled())
+                .isEqualTo(CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        assertThat(metadata.getConnectSuccessCompanionAppNotInstalled())
+                .isEqualTo(CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+        assertThat(metadata.getDeviceType()).isEqualTo(DEVICE_TYPE);
+        assertThat(metadata.getDownloadCompanionAppDescription())
+                .isEqualTo(DOWNLOAD_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getFailConnectGoToSettingsDescription())
+                .isEqualTo(FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+        assertThat(metadata.getImage()).isEqualTo(IMAGE);
+        assertThat(metadata.getImageUrl()).isEqualTo(IMAGE_URL);
+        assertThat(metadata.getInitialNotificationDescription())
+                .isEqualTo(INITIAL_NOTIFICATION_DESCRIPTION);
+        assertThat(metadata.getInitialNotificationDescriptionNoAccount())
+                .isEqualTo(INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        assertThat(metadata.getInitialPairingDescription()).isEqualTo(INITIAL_PAIRING_DESCRIPTION);
+        assertThat(metadata.getIntentUri()).isEqualTo(INTENT_URI);
+        assertThat(metadata.getName()).isEqualTo(NAME);
+        assertThat(metadata.getOpenCompanionAppDescription())
+                .isEqualTo(OPEN_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getRetroactivePairingDescription())
+                .isEqualTo(RETRO_ACTIVE_PAIRING_DESCRIPTION);
+        assertThat(metadata.getSubsequentPairingDescription())
+                .isEqualTo(SUBSEQUENT_PAIRING_DESCRIPTION);
+        assertThat(metadata.getTriggerDistance()).isWithin(DELTA).of(TRIGGER_DISTANCE);
+        assertThat(metadata.getTrueWirelessImageUrlCase()).isEqualTo(TRUE_WIRELESS_IMAGE_URL_CASE);
+        assertThat(metadata.getTrueWirelessImageUrlLeftBud())
+                .isEqualTo(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        assertThat(metadata.getTrueWirelessImageUrlRightBud())
+                .isEqualTo(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+        assertThat(metadata.getUnableToConnectDescription())
+                .isEqualTo(UNABLE_TO_CONNECT_DESCRIPTION);
+        assertThat(metadata.getUnableToConnectTitle()).isEqualTo(UNABLE_TO_CONNECT_TITLE);
+        assertThat(metadata.getUpdateCompanionAppDescription())
+                .isEqualTo(UPDATE_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getWaitLaunchCompanionAppDescription())
+                .isEqualTo(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+    }
+
+    /* Generates FastPairAntispoofKeyDeviceMetadata. */
+    private static FastPairAntispoofKeyDeviceMetadata genFastPairAntispoofKeyDeviceMetadata(
+            byte[] antispoofPublicKey, FastPairDeviceMetadata deviceMetadata) {
+        FastPairAntispoofKeyDeviceMetadata.Builder builder =
+                new FastPairAntispoofKeyDeviceMetadata.Builder();
+        builder.setAntispoofPublicKey(antispoofPublicKey);
+        builder.setFastPairDeviceMetadata(deviceMetadata);
+
+        return builder.build();
+    }
+
+    /* Generates FastPairDeviceMetadata. */
+    private static FastPairDeviceMetadata genFastPairDeviceMetadata() {
+        FastPairDeviceMetadata.Builder builder = new FastPairDeviceMetadata.Builder();
+        builder.setBleTxPower(BLE_TX_POWER);
+        builder.setConnectSuccessCompanionAppInstalled(CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        builder.setConnectSuccessCompanionAppNotInstalled(
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+        builder.setDeviceType(DEVICE_TYPE);
+        builder.setDownloadCompanionAppDescription(DOWNLOAD_COMPANION_APP_DESCRIPTION);
+        builder.setFailConnectGoToSettingsDescription(FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+        builder.setImage(IMAGE);
+        builder.setImageUrl(IMAGE_URL);
+        builder.setInitialNotificationDescription(INITIAL_NOTIFICATION_DESCRIPTION);
+        builder.setInitialNotificationDescriptionNoAccount(
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        builder.setInitialPairingDescription(INITIAL_PAIRING_DESCRIPTION);
+        builder.setIntentUri(INTENT_URI);
+        builder.setName(NAME);
+        builder.setOpenCompanionAppDescription(OPEN_COMPANION_APP_DESCRIPTION);
+        builder.setRetroactivePairingDescription(RETRO_ACTIVE_PAIRING_DESCRIPTION);
+        builder.setSubsequentPairingDescription(SUBSEQUENT_PAIRING_DESCRIPTION);
+        builder.setTriggerDistance(TRIGGER_DISTANCE);
+        builder.setTrueWirelessImageUrlCase(TRUE_WIRELESS_IMAGE_URL_CASE);
+        builder.setTrueWirelessImageUrlLeftBud(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        builder.setTrueWirelessImageUrlRightBud(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+        builder.setUnableToConnectDescription(UNABLE_TO_CONNECT_DESCRIPTION);
+        builder.setUnableToConnectTitle(UNABLE_TO_CONNECT_TITLE);
+        builder.setUpdateCompanionAppDescription(UPDATE_COMPANION_APP_DESCRIPTION);
+        builder.setWaitLaunchCompanionAppDescription(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+
+        return builder.build();
+    }
+}
diff --git a/nearby/tests/unit/src/android/nearby/FastPairDataProviderServiceTest.java b/nearby/tests/unit/src/android/nearby/FastPairDataProviderServiceTest.java
new file mode 100644
index 0000000..b3f2442
--- /dev/null
+++ b/nearby/tests/unit/src/android/nearby/FastPairDataProviderServiceTest.java
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2022 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 android.nearby;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.nearby.aidl.ByteArrayParcel;
+import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
+import android.nearby.aidl.FastPairAccountKeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairDeviceMetadataParcel;
+import android.nearby.aidl.FastPairDiscoveryItemParcel;
+import android.nearby.aidl.FastPairEligibleAccountParcel;
+import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
+import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
+import android.nearby.aidl.FastPairManageAccountRequestParcel;
+import android.nearby.aidl.IFastPairAccountDevicesMetadataCallback;
+import android.nearby.aidl.IFastPairAntispoofKeyDeviceMetadataCallback;
+import android.nearby.aidl.IFastPairDataProvider;
+import android.nearby.aidl.IFastPairEligibleAccountsCallback;
+import android.nearby.aidl.IFastPairManageAccountCallback;
+import android.nearby.aidl.IFastPairManageAccountDeviceCallback;
+
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class FastPairDataProviderServiceTest {
+
+    private static final String TAG = "FastPairDataProviderServiceTest";
+
+    private static final int BLE_TX_POWER  = 5;
+    private static final String CONNECT_SUCCESS_COMPANION_APP_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_INSTALLED";
+    private static final String CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED";
+    private static final float DELTA = 0.001f;
+    private static final int DEVICE_TYPE = 7;
+    private static final String DOWNLOAD_COMPANION_APP_DESCRIPTION =
+            "DOWNLOAD_COMPANION_APP_DESCRIPTION";
+    private static final Account ELIGIBLE_ACCOUNT_1 = new Account("abc@google.com", "type1");
+    private static final boolean ELIGIBLE_ACCOUNT_1_OPT_IN = true;
+    private static final Account ELIGIBLE_ACCOUNT_2 = new Account("def@gmail.com", "type2");
+    private static final boolean ELIGIBLE_ACCOUNT_2_OPT_IN = false;
+    private static final Account MANAGE_ACCOUNT = new Account("ghi@gmail.com", "type3");
+    private static final Account ACCOUNTDEVICES_METADATA_ACCOUNT =
+            new Account("jk@gmail.com", "type4");
+    private static final int NUM_ACCOUNT_DEVICES = 2;
+
+    private static final int ERROR_CODE_BAD_REQUEST =
+            FastPairDataProviderService.ERROR_CODE_BAD_REQUEST;
+    private static final int MANAGE_ACCOUNT_REQUEST_TYPE =
+            FastPairDataProviderService.MANAGE_REQUEST_ADD;
+    private static final String ERROR_STRING = "ERROR_STRING";
+    private static final String FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION =
+            "FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION";
+    private static final byte[] IMAGE = new byte[] {7, 9};
+    private static final String IMAGE_URL = "IMAGE_URL";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION =
+            "INITIAL_NOTIFICATION_DESCRIPTION";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT =
+            "INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT";
+    private static final String INITIAL_PAIRING_DESCRIPTION = "INITIAL_PAIRING_DESCRIPTION";
+    private static final String INTENT_URI = "INTENT_URI";
+    private static final String OPEN_COMPANION_APP_DESCRIPTION = "OPEN_COMPANION_APP_DESCRIPTION";
+    private static final String RETRO_ACTIVE_PAIRING_DESCRIPTION =
+            "RETRO_ACTIVE_PAIRING_DESCRIPTION";
+    private static final String SUBSEQUENT_PAIRING_DESCRIPTION = "SUBSEQUENT_PAIRING_DESCRIPTION";
+    private static final float TRIGGER_DISTANCE = 111;
+    private static final String TRUE_WIRELESS_IMAGE_URL_CASE = "TRUE_WIRELESS_IMAGE_URL_CASE";
+    private static final String TRUE_WIRELESS_IMAGE_URL_LEFT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_LEFT_BUD";
+    private static final String TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD";
+    private static final String UNABLE_TO_CONNECT_DESCRIPTION = "UNABLE_TO_CONNECT_DESCRIPTION";
+    private static final String UNABLE_TO_CONNECT_TITLE = "UNABLE_TO_CONNECT_TITLE";
+    private static final String UPDATE_COMPANION_APP_DESCRIPTION =
+            "UPDATE_COMPANION_APP_DESCRIPTION";
+    private static final String WAIT_LAUNCH_COMPANION_APP_DESCRIPTION =
+            "WAIT_LAUNCH_COMPANION_APP_DESCRIPTION";
+    private static final byte[] ACCOUNT_KEY = new byte[] {3};
+    private static final byte[] ACCOUNT_KEY_2 = new byte[] {9, 3};
+    private static final byte[] SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS = new byte[] {2, 8};
+    private static final byte[] REQUEST_MODEL_ID = new byte[] {1, 2, 3};
+    private static final byte[] ANTI_SPOOFING_KEY = new byte[] {4, 5, 6};
+    private static final String ACTION_URL = "ACTION_URL";
+    private static final int ACTION_URL_TYPE = 5;
+    private static final String APP_NAME = "APP_NAME";
+    private static final byte[] AUTHENTICATION_PUBLIC_KEY_SEC_P256R1 = new byte[] {5, 7};
+    private static final String DESCRIPTION = "DESCRIPTION";
+    private static final String DEVICE_NAME = "DEVICE_NAME";
+    private static final String DISPLAY_URL = "DISPLAY_URL";
+    private static final long FIRST_OBSERVATION_TIMESTAMP_MILLIS = 8393L;
+    private static final String  ICON_FIFE_URL = "ICON_FIFE_URL";
+    private static final byte[]  ICON_PNG = new byte[]{2, 5};
+    private static final String ID = "ID";
+    private static final long LAST_OBSERVATION_TIMESTAMP_MILLIS = 934234L;
+    private static final String MAC_ADDRESS = "MAC_ADDRESS";
+    private static final String NAME = "NAME";
+    private static final String PACKAGE_NAME = "PACKAGE_NAME";
+    private static final long PENDING_APP_INSTALL_TIMESTAMP_MILLIS = 832393L;
+    private static final int RSSI = 9;
+    private static final int STATE = 63;
+    private static final String TITLE = "TITLE";
+    private static final String TRIGGER_ID = "TRIGGER_ID";
+    private static final int TX_POWER = 62;
+
+    private static final int ELIGIBLE_ACCOUNTS_NUM = 2;
+    private static final ImmutableList<FastPairEligibleAccount> ELIGIBLE_ACCOUNTS =
+            ImmutableList.of(
+                    genHappyPathFastPairEligibleAccount(ELIGIBLE_ACCOUNT_1,
+                            ELIGIBLE_ACCOUNT_1_OPT_IN),
+                    genHappyPathFastPairEligibleAccount(ELIGIBLE_ACCOUNT_2,
+                            ELIGIBLE_ACCOUNT_2_OPT_IN));
+    private static final int ACCOUNTKEY_DEVICE_NUM = 2;
+    private static final ImmutableList<FastPairAccountKeyDeviceMetadata>
+            FAST_PAIR_ACCOUNT_DEVICES_METADATA =
+            ImmutableList.of(
+                    genHappyPathFastPairAccountkeyDeviceMetadata(),
+                    genHappyPathFastPairAccountkeyDeviceMetadata());
+
+    private static final FastPairAntispoofKeyDeviceMetadataRequestParcel
+            FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL =
+            genFastPairAntispoofKeyDeviceMetadataRequestParcel();
+    private static final FastPairAccountDevicesMetadataRequestParcel
+            FAST_PAIR_ACCOUNT_DEVICES_METADATA_REQUEST_PARCEL =
+            genFastPairAccountDevicesMetadataRequestParcel();
+    private static final FastPairEligibleAccountsRequestParcel
+            FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL =
+            genFastPairEligibleAccountsRequestParcel();
+    private static final FastPairManageAccountRequestParcel
+            FAST_PAIR_MANAGE_ACCOUNT_REQUEST_PARCEL =
+            genFastPairManageAccountRequestParcel();
+    private static final FastPairManageAccountDeviceRequestParcel
+            FAST_PAIR_MANAGE_ACCOUNT_DEVICE_REQUEST_PARCEL =
+            genFastPairManageAccountDeviceRequestParcel();
+    private static final FastPairAntispoofKeyDeviceMetadata
+            HAPPY_PATH_FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA =
+            genHappyPathFastPairAntispoofKeyDeviceMetadata();
+
+    @Captor private ArgumentCaptor<FastPairEligibleAccountParcel[]>
+            mFastPairEligibleAccountParcelsArgumentCaptor;
+    @Captor private ArgumentCaptor<FastPairAccountKeyDeviceMetadataParcel[]>
+            mFastPairAccountKeyDeviceMetadataParcelsArgumentCaptor;
+
+    @Mock private FastPairDataProviderService mMockFastPairDataProviderService;
+    @Mock private IFastPairAntispoofKeyDeviceMetadataCallback.Stub
+            mAntispoofKeyDeviceMetadataCallback;
+    @Mock private IFastPairAccountDevicesMetadataCallback.Stub mAccountDevicesMetadataCallback;
+    @Mock private IFastPairEligibleAccountsCallback.Stub mEligibleAccountsCallback;
+    @Mock private IFastPairManageAccountCallback.Stub mManageAccountCallback;
+    @Mock private IFastPairManageAccountDeviceCallback.Stub mManageAccountDeviceCallback;
+
+    private MyHappyPathProvider mHappyPathFastPairDataProvider;
+    private MyErrorPathProvider mErrorPathFastPairDataProvider;
+
+    @Before
+    public void setUp() throws Exception {
+        initMocks(this);
+
+        mHappyPathFastPairDataProvider =
+                new MyHappyPathProvider(TAG, mMockFastPairDataProviderService);
+        mErrorPathFastPairDataProvider =
+                new MyErrorPathProvider(TAG, mMockFastPairDataProviderService);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHappyPathLoadFastPairAntispoofKeyDeviceMetadata() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
+        mHappyPathFastPairDataProvider.asProvider().loadFastPairAntispoofKeyDeviceMetadata(
+                FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL,
+                mAntispoofKeyDeviceMetadataCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataRequest>
+                fastPairAntispoofKeyDeviceMetadataRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataRequest.class
+                );
+        verify(mMockFastPairDataProviderService).onLoadFastPairAntispoofKeyDeviceMetadata(
+                fastPairAntispoofKeyDeviceMetadataRequestCaptor.capture(),
+                any(FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataCallback.class));
+        ensureHappyPathAsExpected(fastPairAntispoofKeyDeviceMetadataRequestCaptor.getValue());
+
+        // AOSP receives responses and verifies that it is as expected.
+        final ArgumentCaptor<FastPairAntispoofKeyDeviceMetadataParcel>
+                fastPairAntispoofKeyDeviceMetadataParcelCaptor =
+                ArgumentCaptor.forClass(FastPairAntispoofKeyDeviceMetadataParcel.class);
+        verify(mAntispoofKeyDeviceMetadataCallback).onFastPairAntispoofKeyDeviceMetadataReceived(
+                fastPairAntispoofKeyDeviceMetadataParcelCaptor.capture());
+        ensureHappyPathAsExpected(fastPairAntispoofKeyDeviceMetadataParcelCaptor.getValue());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHappyPathLoadFastPairAccountDevicesMetadata() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
+        mHappyPathFastPairDataProvider.asProvider().loadFastPairAccountDevicesMetadata(
+                FAST_PAIR_ACCOUNT_DEVICES_METADATA_REQUEST_PARCEL,
+                mAccountDevicesMetadataCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderService.FastPairAccountDevicesMetadataRequest>
+                fastPairAccountDevicesMetadataRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderService.FastPairAccountDevicesMetadataRequest.class);
+        verify(mMockFastPairDataProviderService).onLoadFastPairAccountDevicesMetadata(
+                fastPairAccountDevicesMetadataRequestCaptor.capture(),
+                any(FastPairDataProviderService.FastPairAccountDevicesMetadataCallback.class));
+        ensureHappyPathAsExpected(fastPairAccountDevicesMetadataRequestCaptor.getValue());
+
+        // AOSP receives responses and verifies that it is as expected.
+        verify(mAccountDevicesMetadataCallback).onFastPairAccountDevicesMetadataReceived(
+                mFastPairAccountKeyDeviceMetadataParcelsArgumentCaptor.capture());
+        ensureHappyPathAsExpected(
+                mFastPairAccountKeyDeviceMetadataParcelsArgumentCaptor.getValue());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHappyPathLoadFastPairEligibleAccounts() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
+        mHappyPathFastPairDataProvider.asProvider().loadFastPairEligibleAccounts(
+                FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL,
+                mEligibleAccountsCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderService.FastPairEligibleAccountsRequest>
+                fastPairEligibleAccountsRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderService.FastPairEligibleAccountsRequest.class);
+        verify(mMockFastPairDataProviderService).onLoadFastPairEligibleAccounts(
+                fastPairEligibleAccountsRequestCaptor.capture(),
+                any(FastPairDataProviderService.FastPairEligibleAccountsCallback.class));
+        ensureHappyPathAsExpected(fastPairEligibleAccountsRequestCaptor.getValue());
+
+        // AOSP receives responses and verifies that it is as expected.
+        verify(mEligibleAccountsCallback).onFastPairEligibleAccountsReceived(
+                mFastPairEligibleAccountParcelsArgumentCaptor.capture());
+        ensureHappyPathAsExpected(mFastPairEligibleAccountParcelsArgumentCaptor.getValue());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHappyPathManageFastPairAccount() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
+        mHappyPathFastPairDataProvider.asProvider().manageFastPairAccount(
+                FAST_PAIR_MANAGE_ACCOUNT_REQUEST_PARCEL,
+                mManageAccountCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderService.FastPairManageAccountRequest>
+                fastPairManageAccountRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderService.FastPairManageAccountRequest.class);
+        verify(mMockFastPairDataProviderService).onManageFastPairAccount(
+                fastPairManageAccountRequestCaptor.capture(),
+                any(FastPairDataProviderService.FastPairManageActionCallback.class));
+        ensureHappyPathAsExpected(fastPairManageAccountRequestCaptor.getValue());
+
+        // AOSP receives SUCCESS response.
+        verify(mManageAccountCallback).onSuccess();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHappyPathManageFastPairAccountDevice() throws Exception {
+        // AOSP sends calls to OEM via Parcelable.
+        mHappyPathFastPairDataProvider.asProvider().manageFastPairAccountDevice(
+                FAST_PAIR_MANAGE_ACCOUNT_DEVICE_REQUEST_PARCEL,
+                mManageAccountDeviceCallback);
+
+        // OEM receives request and verifies that it is as expected.
+        final ArgumentCaptor<FastPairDataProviderService.FastPairManageAccountDeviceRequest>
+                fastPairManageAccountDeviceRequestCaptor =
+                ArgumentCaptor.forClass(
+                        FastPairDataProviderService.FastPairManageAccountDeviceRequest.class);
+        verify(mMockFastPairDataProviderService).onManageFastPairAccountDevice(
+                fastPairManageAccountDeviceRequestCaptor.capture(),
+                any(FastPairDataProviderService.FastPairManageActionCallback.class));
+        ensureHappyPathAsExpected(fastPairManageAccountDeviceRequestCaptor.getValue());
+
+        // AOSP receives SUCCESS response.
+        verify(mManageAccountDeviceCallback).onSuccess();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testErrorPathLoadFastPairAntispoofKeyDeviceMetadata() throws Exception {
+        mErrorPathFastPairDataProvider.asProvider().loadFastPairAntispoofKeyDeviceMetadata(
+                FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA_REQUEST_PARCEL,
+                mAntispoofKeyDeviceMetadataCallback);
+        verify(mMockFastPairDataProviderService).onLoadFastPairAntispoofKeyDeviceMetadata(
+                any(FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataRequest.class),
+                any(FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataCallback.class));
+        verify(mAntispoofKeyDeviceMetadataCallback).onError(
+                eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testErrorPathLoadFastPairAccountDevicesMetadata() throws Exception {
+        mErrorPathFastPairDataProvider.asProvider().loadFastPairAccountDevicesMetadata(
+                FAST_PAIR_ACCOUNT_DEVICES_METADATA_REQUEST_PARCEL,
+                mAccountDevicesMetadataCallback);
+        verify(mMockFastPairDataProviderService).onLoadFastPairAccountDevicesMetadata(
+                any(FastPairDataProviderService.FastPairAccountDevicesMetadataRequest.class),
+                any(FastPairDataProviderService.FastPairAccountDevicesMetadataCallback.class));
+        verify(mAccountDevicesMetadataCallback).onError(
+                eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testErrorPathLoadFastPairEligibleAccounts() throws Exception {
+        mErrorPathFastPairDataProvider.asProvider().loadFastPairEligibleAccounts(
+                FAST_PAIR_ELIGIBLE_ACCOUNTS_REQUEST_PARCEL,
+                mEligibleAccountsCallback);
+        verify(mMockFastPairDataProviderService).onLoadFastPairEligibleAccounts(
+                any(FastPairDataProviderService.FastPairEligibleAccountsRequest.class),
+                any(FastPairDataProviderService.FastPairEligibleAccountsCallback.class));
+        verify(mEligibleAccountsCallback).onError(
+                eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testErrorPathManageFastPairAccount() throws Exception {
+        mErrorPathFastPairDataProvider.asProvider().manageFastPairAccount(
+                FAST_PAIR_MANAGE_ACCOUNT_REQUEST_PARCEL,
+                mManageAccountCallback);
+        verify(mMockFastPairDataProviderService).onManageFastPairAccount(
+                any(FastPairDataProviderService.FastPairManageAccountRequest.class),
+                any(FastPairDataProviderService.FastPairManageActionCallback.class));
+        verify(mManageAccountCallback).onError(eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testErrorPathManageFastPairAccountDevice() throws Exception {
+        mErrorPathFastPairDataProvider.asProvider().manageFastPairAccountDevice(
+                FAST_PAIR_MANAGE_ACCOUNT_DEVICE_REQUEST_PARCEL,
+                mManageAccountDeviceCallback);
+        verify(mMockFastPairDataProviderService).onManageFastPairAccountDevice(
+                any(FastPairDataProviderService.FastPairManageAccountDeviceRequest.class),
+                any(FastPairDataProviderService.FastPairManageActionCallback.class));
+        verify(mManageAccountDeviceCallback).onError(eq(ERROR_CODE_BAD_REQUEST), eq(ERROR_STRING));
+    }
+
+    public static class MyHappyPathProvider extends FastPairDataProviderService {
+
+        private final FastPairDataProviderService mMockFastPairDataProviderService;
+
+        public MyHappyPathProvider(@NonNull String tag, FastPairDataProviderService mock) {
+            super(tag);
+            mMockFastPairDataProviderService = mock;
+        }
+
+        public IFastPairDataProvider asProvider() {
+            Intent intent = new Intent();
+            return IFastPairDataProvider.Stub.asInterface(onBind(intent));
+        }
+
+        @Override
+        public void onLoadFastPairAntispoofKeyDeviceMetadata(
+                @NonNull FastPairAntispoofKeyDeviceMetadataRequest request,
+                @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairAntispoofKeyDeviceMetadata(
+                    request, callback);
+            callback.onFastPairAntispoofKeyDeviceMetadataReceived(
+                    HAPPY_PATH_FAST_PAIR_ANTI_SPOOF_KEY_DEVICE_METADATA);
+        }
+
+        @Override
+        public void onLoadFastPairAccountDevicesMetadata(
+                @NonNull FastPairAccountDevicesMetadataRequest request,
+                @NonNull FastPairAccountDevicesMetadataCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairAccountDevicesMetadata(
+                    request, callback);
+            callback.onFastPairAccountDevicesMetadataReceived(FAST_PAIR_ACCOUNT_DEVICES_METADATA);
+        }
+
+        @Override
+        public void onLoadFastPairEligibleAccounts(
+                @NonNull FastPairEligibleAccountsRequest request,
+                @NonNull FastPairEligibleAccountsCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairEligibleAccounts(
+                    request, callback);
+            callback.onFastPairEligibleAccountsReceived(ELIGIBLE_ACCOUNTS);
+        }
+
+        @Override
+        public void onManageFastPairAccount(
+                @NonNull FastPairManageAccountRequest request,
+                @NonNull FastPairManageActionCallback callback) {
+            mMockFastPairDataProviderService.onManageFastPairAccount(request, callback);
+            callback.onSuccess();
+        }
+
+        @Override
+        public void onManageFastPairAccountDevice(
+                @NonNull FastPairManageAccountDeviceRequest request,
+                @NonNull FastPairManageActionCallback callback) {
+            mMockFastPairDataProviderService.onManageFastPairAccountDevice(request, callback);
+            callback.onSuccess();
+        }
+    }
+
+    public static class MyErrorPathProvider extends FastPairDataProviderService {
+
+        private final FastPairDataProviderService mMockFastPairDataProviderService;
+
+        public MyErrorPathProvider(@NonNull String tag, FastPairDataProviderService mock) {
+            super(tag);
+            mMockFastPairDataProviderService = mock;
+        }
+
+        public IFastPairDataProvider asProvider() {
+            Intent intent = new Intent();
+            return IFastPairDataProvider.Stub.asInterface(onBind(intent));
+        }
+
+        @Override
+        public void onLoadFastPairAntispoofKeyDeviceMetadata(
+                @NonNull FastPairAntispoofKeyDeviceMetadataRequest request,
+                @NonNull FastPairAntispoofKeyDeviceMetadataCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairAntispoofKeyDeviceMetadata(
+                    request, callback);
+            callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
+        }
+
+        @Override
+        public void onLoadFastPairAccountDevicesMetadata(
+                @NonNull FastPairAccountDevicesMetadataRequest request,
+                @NonNull FastPairAccountDevicesMetadataCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairAccountDevicesMetadata(
+                    request, callback);
+            callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
+        }
+
+        @Override
+        public void onLoadFastPairEligibleAccounts(
+                @NonNull FastPairEligibleAccountsRequest request,
+                @NonNull FastPairEligibleAccountsCallback callback) {
+            mMockFastPairDataProviderService.onLoadFastPairEligibleAccounts(request, callback);
+            callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
+        }
+
+        @Override
+        public void onManageFastPairAccount(
+                @NonNull FastPairManageAccountRequest request,
+                @NonNull FastPairManageActionCallback callback) {
+            mMockFastPairDataProviderService.onManageFastPairAccount(request, callback);
+            callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
+        }
+
+        @Override
+        public void onManageFastPairAccountDevice(
+                @NonNull FastPairManageAccountDeviceRequest request,
+                @NonNull FastPairManageActionCallback callback) {
+            mMockFastPairDataProviderService.onManageFastPairAccountDevice(request, callback);
+            callback.onError(ERROR_CODE_BAD_REQUEST, ERROR_STRING);
+        }
+    }
+
+    /* Generates AntispoofKeyDeviceMetadataRequestParcel. */
+    private static FastPairAntispoofKeyDeviceMetadataRequestParcel
+            genFastPairAntispoofKeyDeviceMetadataRequestParcel() {
+        FastPairAntispoofKeyDeviceMetadataRequestParcel requestParcel =
+                new FastPairAntispoofKeyDeviceMetadataRequestParcel();
+        requestParcel.modelId = REQUEST_MODEL_ID;
+
+        return requestParcel;
+    }
+
+    /* Generates AccountDevicesMetadataRequestParcel. */
+    private static FastPairAccountDevicesMetadataRequestParcel
+            genFastPairAccountDevicesMetadataRequestParcel() {
+        FastPairAccountDevicesMetadataRequestParcel requestParcel =
+                new FastPairAccountDevicesMetadataRequestParcel();
+
+        requestParcel.account = ACCOUNTDEVICES_METADATA_ACCOUNT;
+        requestParcel.deviceAccountKeys = new ByteArrayParcel[NUM_ACCOUNT_DEVICES];
+        requestParcel.deviceAccountKeys[0] = new ByteArrayParcel();
+        requestParcel.deviceAccountKeys[1] = new ByteArrayParcel();
+        requestParcel.deviceAccountKeys[0].byteArray = ACCOUNT_KEY;
+        requestParcel.deviceAccountKeys[1].byteArray = ACCOUNT_KEY_2;
+
+        return requestParcel;
+    }
+
+    /* Generates FastPairEligibleAccountsRequestParcel. */
+    private static FastPairEligibleAccountsRequestParcel
+            genFastPairEligibleAccountsRequestParcel() {
+        FastPairEligibleAccountsRequestParcel requestParcel =
+                new FastPairEligibleAccountsRequestParcel();
+        // No fields since FastPairEligibleAccountsRequestParcel is just a place holder now.
+        return requestParcel;
+    }
+
+    /* Generates FastPairManageAccountRequestParcel. */
+    private static FastPairManageAccountRequestParcel
+            genFastPairManageAccountRequestParcel() {
+        FastPairManageAccountRequestParcel requestParcel =
+                new FastPairManageAccountRequestParcel();
+        requestParcel.account = MANAGE_ACCOUNT;
+        requestParcel.requestType = MANAGE_ACCOUNT_REQUEST_TYPE;
+
+        return requestParcel;
+    }
+
+    /* Generates FastPairManageAccountDeviceRequestParcel. */
+    private static FastPairManageAccountDeviceRequestParcel
+            genFastPairManageAccountDeviceRequestParcel() {
+        FastPairManageAccountDeviceRequestParcel requestParcel =
+                new FastPairManageAccountDeviceRequestParcel();
+        requestParcel.account = MANAGE_ACCOUNT;
+        requestParcel.requestType = MANAGE_ACCOUNT_REQUEST_TYPE;
+        requestParcel.accountKeyDeviceMetadata =
+                genHappyPathFastPairAccountkeyDeviceMetadataParcel();
+
+        return requestParcel;
+    }
+
+    /* Generates Happy Path AntispoofKeyDeviceMetadata. */
+    private static FastPairAntispoofKeyDeviceMetadata
+            genHappyPathFastPairAntispoofKeyDeviceMetadata() {
+        FastPairAntispoofKeyDeviceMetadata.Builder builder =
+                new FastPairAntispoofKeyDeviceMetadata.Builder();
+        builder.setAntispoofPublicKey(ANTI_SPOOFING_KEY);
+        builder.setFastPairDeviceMetadata(genHappyPathFastPairDeviceMetadata());
+
+        return builder.build();
+    }
+
+    /* Generates Happy Path FastPairAccountKeyDeviceMetadata. */
+    private static FastPairAccountKeyDeviceMetadata
+            genHappyPathFastPairAccountkeyDeviceMetadata() {
+        FastPairAccountKeyDeviceMetadata.Builder builder =
+                new FastPairAccountKeyDeviceMetadata.Builder();
+        builder.setDeviceAccountKey(ACCOUNT_KEY);
+        builder.setFastPairDeviceMetadata(genHappyPathFastPairDeviceMetadata());
+        builder.setSha256DeviceAccountKeyPublicAddress(SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS);
+        builder.setFastPairDiscoveryItem(genHappyPathFastPairDiscoveryItem());
+
+        return builder.build();
+    }
+
+    /* Generates Happy Path FastPairAccountKeyDeviceMetadataParcel. */
+    private static FastPairAccountKeyDeviceMetadataParcel
+            genHappyPathFastPairAccountkeyDeviceMetadataParcel() {
+        FastPairAccountKeyDeviceMetadataParcel parcel =
+                new FastPairAccountKeyDeviceMetadataParcel();
+        parcel.deviceAccountKey = ACCOUNT_KEY;
+        parcel.metadata = genHappyPathFastPairDeviceMetadataParcel();
+        parcel.sha256DeviceAccountKeyPublicAddress = SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS;
+        parcel.discoveryItem = genHappyPathFastPairDiscoveryItemParcel();
+
+        return parcel;
+    }
+
+    /* Generates Happy Path DiscoveryItem. */
+    private static FastPairDiscoveryItem genHappyPathFastPairDiscoveryItem() {
+        FastPairDiscoveryItem.Builder builder = new FastPairDiscoveryItem.Builder();
+
+        builder.setActionUrl(ACTION_URL);
+        builder.setActionUrlType(ACTION_URL_TYPE);
+        builder.setAppName(APP_NAME);
+        builder.setAuthenticationPublicKeySecp256r1(AUTHENTICATION_PUBLIC_KEY_SEC_P256R1);
+        builder.setDescription(DESCRIPTION);
+        builder.setDeviceName(DEVICE_NAME);
+        builder.setDisplayUrl(DISPLAY_URL);
+        builder.setFirstObservationTimestampMillis(FIRST_OBSERVATION_TIMESTAMP_MILLIS);
+        builder.setIconFfeUrl(ICON_FIFE_URL);
+        builder.setIconPng(ICON_PNG);
+        builder.setId(ID);
+        builder.setLastObservationTimestampMillis(LAST_OBSERVATION_TIMESTAMP_MILLIS);
+        builder.setMacAddress(MAC_ADDRESS);
+        builder.setPackageName(PACKAGE_NAME);
+        builder.setPendingAppInstallTimestampMillis(PENDING_APP_INSTALL_TIMESTAMP_MILLIS);
+        builder.setRssi(RSSI);
+        builder.setState(STATE);
+        builder.setTitle(TITLE);
+        builder.setTriggerId(TRIGGER_ID);
+        builder.setTxPower(TX_POWER);
+
+        return builder.build();
+    }
+
+    /* Generates Happy Path DiscoveryItemParcel. */
+    private static FastPairDiscoveryItemParcel genHappyPathFastPairDiscoveryItemParcel() {
+        FastPairDiscoveryItemParcel parcel = new FastPairDiscoveryItemParcel();
+
+        parcel.actionUrl = ACTION_URL;
+        parcel.actionUrlType = ACTION_URL_TYPE;
+        parcel.appName = APP_NAME;
+        parcel.authenticationPublicKeySecp256r1 = AUTHENTICATION_PUBLIC_KEY_SEC_P256R1;
+        parcel.description = DESCRIPTION;
+        parcel.deviceName = DEVICE_NAME;
+        parcel.displayUrl = DISPLAY_URL;
+        parcel.firstObservationTimestampMillis = FIRST_OBSERVATION_TIMESTAMP_MILLIS;
+        parcel.iconFifeUrl = ICON_FIFE_URL;
+        parcel.iconPng = ICON_PNG;
+        parcel.id = ID;
+        parcel.lastObservationTimestampMillis = LAST_OBSERVATION_TIMESTAMP_MILLIS;
+        parcel.macAddress = MAC_ADDRESS;
+        parcel.packageName = PACKAGE_NAME;
+        parcel.pendingAppInstallTimestampMillis = PENDING_APP_INSTALL_TIMESTAMP_MILLIS;
+        parcel.rssi = RSSI;
+        parcel.state = STATE;
+        parcel.title = TITLE;
+        parcel.triggerId = TRIGGER_ID;
+        parcel.txPower = TX_POWER;
+
+        return parcel;
+    }
+
+    /* Generates Happy Path DeviceMetadata. */
+    private static FastPairDeviceMetadata genHappyPathFastPairDeviceMetadata() {
+        FastPairDeviceMetadata.Builder builder = new FastPairDeviceMetadata.Builder();
+        builder.setBleTxPower(BLE_TX_POWER);
+        builder.setConnectSuccessCompanionAppInstalled(CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        builder.setConnectSuccessCompanionAppNotInstalled(
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+        builder.setDeviceType(DEVICE_TYPE);
+        builder.setDownloadCompanionAppDescription(DOWNLOAD_COMPANION_APP_DESCRIPTION);
+        builder.setFailConnectGoToSettingsDescription(FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+        builder.setImage(IMAGE);
+        builder.setImageUrl(IMAGE_URL);
+        builder.setInitialNotificationDescription(INITIAL_NOTIFICATION_DESCRIPTION);
+        builder.setInitialNotificationDescriptionNoAccount(
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        builder.setInitialPairingDescription(INITIAL_PAIRING_DESCRIPTION);
+        builder.setIntentUri(INTENT_URI);
+        builder.setName(NAME);
+        builder.setOpenCompanionAppDescription(OPEN_COMPANION_APP_DESCRIPTION);
+        builder.setRetroactivePairingDescription(RETRO_ACTIVE_PAIRING_DESCRIPTION);
+        builder.setSubsequentPairingDescription(SUBSEQUENT_PAIRING_DESCRIPTION);
+        builder.setTriggerDistance(TRIGGER_DISTANCE);
+        builder.setTrueWirelessImageUrlCase(TRUE_WIRELESS_IMAGE_URL_CASE);
+        builder.setTrueWirelessImageUrlLeftBud(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        builder.setTrueWirelessImageUrlRightBud(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+        builder.setUnableToConnectDescription(UNABLE_TO_CONNECT_DESCRIPTION);
+        builder.setUnableToConnectTitle(UNABLE_TO_CONNECT_TITLE);
+        builder.setUpdateCompanionAppDescription(UPDATE_COMPANION_APP_DESCRIPTION);
+        builder.setWaitLaunchCompanionAppDescription(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+
+        return builder.build();
+    }
+
+    /* Generates Happy Path DeviceMetadataParcel. */
+    private static FastPairDeviceMetadataParcel genHappyPathFastPairDeviceMetadataParcel() {
+        FastPairDeviceMetadataParcel parcel = new FastPairDeviceMetadataParcel();
+
+        parcel.bleTxPower = BLE_TX_POWER;
+        parcel.connectSuccessCompanionAppInstalled = CONNECT_SUCCESS_COMPANION_APP_INSTALLED;
+        parcel.connectSuccessCompanionAppNotInstalled =
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED;
+        parcel.deviceType = DEVICE_TYPE;
+        parcel.downloadCompanionAppDescription = DOWNLOAD_COMPANION_APP_DESCRIPTION;
+        parcel.failConnectGoToSettingsDescription = FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION;
+        parcel.image = IMAGE;
+        parcel.imageUrl = IMAGE_URL;
+        parcel.initialNotificationDescription = INITIAL_NOTIFICATION_DESCRIPTION;
+        parcel.initialNotificationDescriptionNoAccount =
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT;
+        parcel.initialPairingDescription = INITIAL_PAIRING_DESCRIPTION;
+        parcel.intentUri = INTENT_URI;
+        parcel.name = NAME;
+        parcel.openCompanionAppDescription = OPEN_COMPANION_APP_DESCRIPTION;
+        parcel.retroactivePairingDescription = RETRO_ACTIVE_PAIRING_DESCRIPTION;
+        parcel.subsequentPairingDescription = SUBSEQUENT_PAIRING_DESCRIPTION;
+        parcel.triggerDistance = TRIGGER_DISTANCE;
+        parcel.trueWirelessImageUrlCase = TRUE_WIRELESS_IMAGE_URL_CASE;
+        parcel.trueWirelessImageUrlLeftBud = TRUE_WIRELESS_IMAGE_URL_LEFT_BUD;
+        parcel.trueWirelessImageUrlRightBud = TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD;
+        parcel.unableToConnectDescription = UNABLE_TO_CONNECT_DESCRIPTION;
+        parcel.unableToConnectTitle = UNABLE_TO_CONNECT_TITLE;
+        parcel.updateCompanionAppDescription = UPDATE_COMPANION_APP_DESCRIPTION;
+        parcel.waitLaunchCompanionAppDescription = WAIT_LAUNCH_COMPANION_APP_DESCRIPTION;
+
+        return parcel;
+    }
+
+    /* Generates Happy Path FastPairEligibleAccount. */
+    private static FastPairEligibleAccount genHappyPathFastPairEligibleAccount(
+            Account account, boolean optIn) {
+        FastPairEligibleAccount.Builder builder = new FastPairEligibleAccount.Builder();
+        builder.setAccount(account);
+        builder.setOptIn(optIn);
+
+        return builder.build();
+    }
+
+    /* Verifies Happy Path AntispoofKeyDeviceMetadataRequest. */
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderService.FastPairAntispoofKeyDeviceMetadataRequest request) {
+        assertThat(request.getModelId()).isEqualTo(REQUEST_MODEL_ID);
+    }
+
+    /* Verifies Happy Path AccountDevicesMetadataRequest. */
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderService.FastPairAccountDevicesMetadataRequest request) {
+        assertThat(request.getAccount()).isEqualTo(ACCOUNTDEVICES_METADATA_ACCOUNT);
+        assertThat(request.getDeviceAccountKeys().size()).isEqualTo(ACCOUNTKEY_DEVICE_NUM);
+        assertThat(request.getDeviceAccountKeys()).contains(ACCOUNT_KEY);
+        assertThat(request.getDeviceAccountKeys()).contains(ACCOUNT_KEY_2);
+    }
+
+    /* Verifies Happy Path FastPairEligibleAccountsRequest. */
+    @SuppressWarnings("UnusedVariable")
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderService.FastPairEligibleAccountsRequest request) {
+        // No fields since FastPairEligibleAccountsRequest is just a place holder now.
+    }
+
+    /* Verifies Happy Path FastPairManageAccountRequest. */
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderService.FastPairManageAccountRequest request) {
+        assertThat(request.getAccount()).isEqualTo(MANAGE_ACCOUNT);
+        assertThat(request.getRequestType()).isEqualTo(MANAGE_ACCOUNT_REQUEST_TYPE);
+    }
+
+    /* Verifies Happy Path FastPairManageAccountDeviceRequest. */
+    private static void ensureHappyPathAsExpected(
+            FastPairDataProviderService.FastPairManageAccountDeviceRequest request) {
+        assertThat(request.getAccount()).isEqualTo(MANAGE_ACCOUNT);
+        assertThat(request.getRequestType()).isEqualTo(MANAGE_ACCOUNT_REQUEST_TYPE);
+        ensureHappyPathAsExpected(request.getAccountKeyDeviceMetadata());
+    }
+
+    /* Verifies Happy Path AntispoofKeyDeviceMetadataParcel. */
+    private static void ensureHappyPathAsExpected(
+            FastPairAntispoofKeyDeviceMetadataParcel metadataParcel) {
+        assertThat(metadataParcel).isNotNull();
+        assertThat(metadataParcel.antispoofPublicKey).isEqualTo(ANTI_SPOOFING_KEY);
+        ensureHappyPathAsExpected(metadataParcel.deviceMetadata);
+    }
+
+    /* Verifies Happy Path FastPairAccountKeyDeviceMetadataParcel[]. */
+    private static void ensureHappyPathAsExpected(
+            FastPairAccountKeyDeviceMetadataParcel[] metadataParcels) {
+        assertThat(metadataParcels).isNotNull();
+        assertThat(metadataParcels).hasLength(ACCOUNTKEY_DEVICE_NUM);
+        for (FastPairAccountKeyDeviceMetadataParcel parcel: metadataParcels) {
+            ensureHappyPathAsExpected(parcel);
+        }
+    }
+
+    /* Verifies Happy Path FastPairAccountKeyDeviceMetadataParcel. */
+    private static void ensureHappyPathAsExpected(
+            FastPairAccountKeyDeviceMetadataParcel metadataParcel) {
+        assertThat(metadataParcel).isNotNull();
+        assertThat(metadataParcel.deviceAccountKey).isEqualTo(ACCOUNT_KEY);
+        assertThat(metadataParcel.sha256DeviceAccountKeyPublicAddress)
+                .isEqualTo(SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS);
+        ensureHappyPathAsExpected(metadataParcel.metadata);
+        ensureHappyPathAsExpected(metadataParcel.discoveryItem);
+    }
+
+    /* Verifies Happy Path FastPairAccountKeyDeviceMetadata. */
+    private static void ensureHappyPathAsExpected(
+            FastPairAccountKeyDeviceMetadata metadata) {
+        assertThat(metadata.getDeviceAccountKey()).isEqualTo(ACCOUNT_KEY);
+        assertThat(metadata.getSha256DeviceAccountKeyPublicAddress())
+                .isEqualTo(SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS);
+        ensureHappyPathAsExpected(metadata.getFastPairDeviceMetadata());
+        ensureHappyPathAsExpected(metadata.getFastPairDiscoveryItem());
+    }
+
+    /* Verifies Happy Path DeviceMetadataParcel. */
+    private static void ensureHappyPathAsExpected(FastPairDeviceMetadataParcel metadataParcel) {
+        assertThat(metadataParcel).isNotNull();
+        assertThat(metadataParcel.bleTxPower).isEqualTo(BLE_TX_POWER);
+
+        assertThat(metadataParcel.connectSuccessCompanionAppInstalled).isEqualTo(
+                CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        assertThat(metadataParcel.connectSuccessCompanionAppNotInstalled).isEqualTo(
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+
+        assertThat(metadataParcel.deviceType).isEqualTo(DEVICE_TYPE);
+        assertThat(metadataParcel.downloadCompanionAppDescription).isEqualTo(
+                DOWNLOAD_COMPANION_APP_DESCRIPTION);
+
+        assertThat(metadataParcel.failConnectGoToSettingsDescription).isEqualTo(
+                FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+
+        assertThat(metadataParcel.image).isEqualTo(IMAGE);
+        assertThat(metadataParcel.imageUrl).isEqualTo(IMAGE_URL);
+        assertThat(metadataParcel.initialNotificationDescription).isEqualTo(
+                INITIAL_NOTIFICATION_DESCRIPTION);
+        assertThat(metadataParcel.initialNotificationDescriptionNoAccount).isEqualTo(
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        assertThat(metadataParcel.initialPairingDescription).isEqualTo(INITIAL_PAIRING_DESCRIPTION);
+        assertThat(metadataParcel.intentUri).isEqualTo(INTENT_URI);
+
+        assertThat(metadataParcel.name).isEqualTo(NAME);
+
+        assertThat(metadataParcel.openCompanionAppDescription).isEqualTo(
+                OPEN_COMPANION_APP_DESCRIPTION);
+
+        assertThat(metadataParcel.retroactivePairingDescription).isEqualTo(
+                RETRO_ACTIVE_PAIRING_DESCRIPTION);
+
+        assertThat(metadataParcel.subsequentPairingDescription).isEqualTo(
+                SUBSEQUENT_PAIRING_DESCRIPTION);
+
+        assertThat(metadataParcel.triggerDistance).isWithin(DELTA).of(TRIGGER_DISTANCE);
+        assertThat(metadataParcel.trueWirelessImageUrlCase).isEqualTo(TRUE_WIRELESS_IMAGE_URL_CASE);
+        assertThat(metadataParcel.trueWirelessImageUrlLeftBud).isEqualTo(
+                TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        assertThat(metadataParcel.trueWirelessImageUrlRightBud).isEqualTo(
+                TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+
+        assertThat(metadataParcel.unableToConnectDescription).isEqualTo(
+                UNABLE_TO_CONNECT_DESCRIPTION);
+        assertThat(metadataParcel.unableToConnectTitle).isEqualTo(UNABLE_TO_CONNECT_TITLE);
+        assertThat(metadataParcel.updateCompanionAppDescription).isEqualTo(
+                UPDATE_COMPANION_APP_DESCRIPTION);
+
+        assertThat(metadataParcel.waitLaunchCompanionAppDescription).isEqualTo(
+                WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+    }
+
+    /* Verifies Happy Path DeviceMetadata. */
+    private static void ensureHappyPathAsExpected(FastPairDeviceMetadata metadata) {
+        assertThat(metadata.getBleTxPower()).isEqualTo(BLE_TX_POWER);
+        assertThat(metadata.getConnectSuccessCompanionAppInstalled())
+                .isEqualTo(CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        assertThat(metadata.getConnectSuccessCompanionAppNotInstalled())
+                .isEqualTo(CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+        assertThat(metadata.getDeviceType()).isEqualTo(DEVICE_TYPE);
+        assertThat(metadata.getDownloadCompanionAppDescription())
+                .isEqualTo(DOWNLOAD_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getFailConnectGoToSettingsDescription())
+                .isEqualTo(FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+        assertThat(metadata.getImage()).isEqualTo(IMAGE);
+        assertThat(metadata.getImageUrl()).isEqualTo(IMAGE_URL);
+        assertThat(metadata.getInitialNotificationDescription())
+                .isEqualTo(INITIAL_NOTIFICATION_DESCRIPTION);
+        assertThat(metadata.getInitialNotificationDescriptionNoAccount())
+                .isEqualTo(INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        assertThat(metadata.getInitialPairingDescription()).isEqualTo(INITIAL_PAIRING_DESCRIPTION);
+        assertThat(metadata.getIntentUri()).isEqualTo(INTENT_URI);
+        assertThat(metadata.getName()).isEqualTo(NAME);
+        assertThat(metadata.getOpenCompanionAppDescription())
+                .isEqualTo(OPEN_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getRetroactivePairingDescription())
+                .isEqualTo(RETRO_ACTIVE_PAIRING_DESCRIPTION);
+        assertThat(metadata.getSubsequentPairingDescription())
+                .isEqualTo(SUBSEQUENT_PAIRING_DESCRIPTION);
+        assertThat(metadata.getTriggerDistance()).isWithin(DELTA).of(TRIGGER_DISTANCE);
+        assertThat(metadata.getTrueWirelessImageUrlCase()).isEqualTo(TRUE_WIRELESS_IMAGE_URL_CASE);
+        assertThat(metadata.getTrueWirelessImageUrlLeftBud())
+                .isEqualTo(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        assertThat(metadata.getTrueWirelessImageUrlRightBud())
+                .isEqualTo(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+        assertThat(metadata.getUnableToConnectDescription())
+                .isEqualTo(UNABLE_TO_CONNECT_DESCRIPTION);
+        assertThat(metadata.getUnableToConnectTitle()).isEqualTo(UNABLE_TO_CONNECT_TITLE);
+        assertThat(metadata.getUpdateCompanionAppDescription())
+                .isEqualTo(UPDATE_COMPANION_APP_DESCRIPTION);
+        assertThat(metadata.getWaitLaunchCompanionAppDescription())
+                .isEqualTo(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+    }
+
+    /* Verifies Happy Path FastPairDiscoveryItemParcel. */
+    private static void ensureHappyPathAsExpected(FastPairDiscoveryItemParcel itemParcel) {
+        assertThat(itemParcel.actionUrl).isEqualTo(ACTION_URL);
+        assertThat(itemParcel.actionUrlType).isEqualTo(ACTION_URL_TYPE);
+        assertThat(itemParcel.appName).isEqualTo(APP_NAME);
+        assertThat(itemParcel.authenticationPublicKeySecp256r1)
+                .isEqualTo(AUTHENTICATION_PUBLIC_KEY_SEC_P256R1);
+        assertThat(itemParcel.description).isEqualTo(DESCRIPTION);
+        assertThat(itemParcel.deviceName).isEqualTo(DEVICE_NAME);
+        assertThat(itemParcel.displayUrl).isEqualTo(DISPLAY_URL);
+        assertThat(itemParcel.firstObservationTimestampMillis)
+                .isEqualTo(FIRST_OBSERVATION_TIMESTAMP_MILLIS);
+        assertThat(itemParcel.iconFifeUrl).isEqualTo(ICON_FIFE_URL);
+        assertThat(itemParcel.iconPng).isEqualTo(ICON_PNG);
+        assertThat(itemParcel.id).isEqualTo(ID);
+        assertThat(itemParcel.lastObservationTimestampMillis)
+                .isEqualTo(LAST_OBSERVATION_TIMESTAMP_MILLIS);
+        assertThat(itemParcel.macAddress).isEqualTo(MAC_ADDRESS);
+        assertThat(itemParcel.packageName).isEqualTo(PACKAGE_NAME);
+        assertThat(itemParcel.pendingAppInstallTimestampMillis)
+                .isEqualTo(PENDING_APP_INSTALL_TIMESTAMP_MILLIS);
+        assertThat(itemParcel.rssi).isEqualTo(RSSI);
+        assertThat(itemParcel.state).isEqualTo(STATE);
+        assertThat(itemParcel.title).isEqualTo(TITLE);
+        assertThat(itemParcel.triggerId).isEqualTo(TRIGGER_ID);
+        assertThat(itemParcel.txPower).isEqualTo(TX_POWER);
+    }
+
+    /* Verifies Happy Path FastPairDiscoveryItem. */
+    private static void ensureHappyPathAsExpected(FastPairDiscoveryItem item) {
+        assertThat(item.getActionUrl()).isEqualTo(ACTION_URL);
+        assertThat(item.getActionUrlType()).isEqualTo(ACTION_URL_TYPE);
+        assertThat(item.getAppName()).isEqualTo(APP_NAME);
+        assertThat(item.getAuthenticationPublicKeySecp256r1())
+                .isEqualTo(AUTHENTICATION_PUBLIC_KEY_SEC_P256R1);
+        assertThat(item.getDescription()).isEqualTo(DESCRIPTION);
+        assertThat(item.getDeviceName()).isEqualTo(DEVICE_NAME);
+        assertThat(item.getDisplayUrl()).isEqualTo(DISPLAY_URL);
+        assertThat(item.getFirstObservationTimestampMillis())
+                .isEqualTo(FIRST_OBSERVATION_TIMESTAMP_MILLIS);
+        assertThat(item.getIconFfeUrl()).isEqualTo(ICON_FIFE_URL);
+        assertThat(item.getIconPng()).isEqualTo(ICON_PNG);
+        assertThat(item.getId()).isEqualTo(ID);
+        assertThat(item.getLastObservationTimestampMillis())
+                .isEqualTo(LAST_OBSERVATION_TIMESTAMP_MILLIS);
+        assertThat(item.getMacAddress()).isEqualTo(MAC_ADDRESS);
+        assertThat(item.getPackageName()).isEqualTo(PACKAGE_NAME);
+        assertThat(item.getPendingAppInstallTimestampMillis())
+                .isEqualTo(PENDING_APP_INSTALL_TIMESTAMP_MILLIS);
+        assertThat(item.getRssi()).isEqualTo(RSSI);
+        assertThat(item.getState()).isEqualTo(STATE);
+        assertThat(item.getTitle()).isEqualTo(TITLE);
+        assertThat(item.getTriggerId()).isEqualTo(TRIGGER_ID);
+        assertThat(item.getTxPower()).isEqualTo(TX_POWER);
+    }
+
+    /* Verifies Happy Path EligibleAccountParcel[]. */
+    private static void ensureHappyPathAsExpected(FastPairEligibleAccountParcel[] accountsParcel) {
+        assertThat(accountsParcel).hasLength(ELIGIBLE_ACCOUNTS_NUM);
+
+        assertThat(accountsParcel[0].account).isEqualTo(ELIGIBLE_ACCOUNT_1);
+        assertThat(accountsParcel[0].optIn).isEqualTo(ELIGIBLE_ACCOUNT_1_OPT_IN);
+
+        assertThat(accountsParcel[1].account).isEqualTo(ELIGIBLE_ACCOUNT_2);
+        assertThat(accountsParcel[1].optIn).isEqualTo(ELIGIBLE_ACCOUNT_2_OPT_IN);
+    }
+}
diff --git a/nearby/tests/unit/src/android/nearby/FastPairDeviceTest.java b/nearby/tests/unit/src/android/nearby/FastPairDeviceTest.java
new file mode 100644
index 0000000..edda3c2
--- /dev/null
+++ b/nearby/tests/unit/src/android/nearby/FastPairDeviceTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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 android.nearby;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FastPairDeviceTest {
+    private static final String NAME = "name";
+    private static final byte[] DATA = new byte[] {0x01, 0x02};
+    private static final String MODEL_ID = "112233";
+    private static final int RSSI = -80;
+    private static final int TX_POWER = -10;
+    private static final String MAC_ADDRESS = "00:11:22:33:44:55";
+    private static List<Integer> sMediums = new ArrayList<Integer>(List.of(1));
+    private static FastPairDevice sDevice;
+
+
+    @Before
+    public void setup() {
+        sDevice = new FastPairDevice(NAME, sMediums, RSSI, TX_POWER, MODEL_ID, MAC_ADDRESS, DATA);
+    }
+
+    @Test
+    public void testParcelable() {
+        Parcel dest = Parcel.obtain();
+        sDevice.writeToParcel(dest, 0);
+        dest.setDataPosition(0);
+        FastPairDevice compareDevice = FastPairDevice.CREATOR.createFromParcel(dest);
+        assertThat(compareDevice.getName()).isEqualTo(NAME);
+        assertThat(compareDevice.getMediums()).isEqualTo(sMediums);
+        assertThat(compareDevice.getRssi()).isEqualTo(RSSI);
+        assertThat(compareDevice.getTxPower()).isEqualTo(TX_POWER);
+        assertThat(compareDevice.getModelId()).isEqualTo(MODEL_ID);
+        assertThat(compareDevice.getBluetoothAddress()).isEqualTo(MAC_ADDRESS);
+        assertThat(compareDevice.getData()).isEqualTo(DATA);
+        assertThat(compareDevice.equals(sDevice)).isTrue();
+        assertThat(compareDevice.hashCode()).isEqualTo(sDevice.hashCode());
+    }
+
+    @Test
+    public void describeContents() {
+        assertThat(sDevice.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void testToString() {
+        assertThat(sDevice.toString()).isEqualTo(
+                "FastPairDevice [name=name, medium={BLE} "
+                        + "rssi=-80 txPower=-10 "
+                        + "modelId=112233 bluetoothAddress=00:11:22:33:44:55]");
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        FastPairDevice[] fastPairDevices = FastPairDevice.CREATOR.newArray(2);
+        assertThat(fastPairDevices.length).isEqualTo(2);
+    }
+
+    @Test
+    public void testBuilder() {
+        FastPairDevice.Builder builder = new FastPairDevice.Builder();
+        FastPairDevice compareDevice = builder.setName(NAME)
+                .addMedium(1)
+                .setBluetoothAddress(MAC_ADDRESS)
+                .setRssi(RSSI)
+                .setTxPower(TX_POWER)
+                .setData(DATA)
+                .setModelId(MODEL_ID)
+                .build();
+        assertThat(compareDevice.getName()).isEqualTo(NAME);
+        assertThat(compareDevice.getMediums()).isEqualTo(sMediums);
+        assertThat(compareDevice.getRssi()).isEqualTo(RSSI);
+        assertThat(compareDevice.getTxPower()).isEqualTo(TX_POWER);
+        assertThat(compareDevice.getModelId()).isEqualTo(MODEL_ID);
+        assertThat(compareDevice.getBluetoothAddress()).isEqualTo(MAC_ADDRESS);
+        assertThat(compareDevice.getData()).isEqualTo(DATA);
+    }
+}
diff --git a/nearby/tests/unit/src/android/nearby/FastPairEligibleAccountTest.java b/nearby/tests/unit/src/android/nearby/FastPairEligibleAccountTest.java
new file mode 100644
index 0000000..da5a518
--- /dev/null
+++ b/nearby/tests/unit/src/android/nearby/FastPairEligibleAccountTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 android.nearby;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.accounts.Account;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class FastPairEligibleAccountTest {
+
+    private static final Account ACCOUNT = new Account("abc@google.com", "type1");
+    private static final Account ACCOUNT_NULL = null;
+
+    private static final boolean OPT_IN_TRUE = true;
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetGetFastPairEligibleAccountNotNull() {
+        FastPairEligibleAccount eligibleAccount =
+                genFastPairEligibleAccount(ACCOUNT, OPT_IN_TRUE);
+
+        assertThat(eligibleAccount.getAccount()).isEqualTo(ACCOUNT);
+        assertThat(eligibleAccount.isOptIn()).isEqualTo(OPT_IN_TRUE);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetGetFastPairEligibleAccountNull() {
+        FastPairEligibleAccount eligibleAccount =
+                genFastPairEligibleAccount(ACCOUNT_NULL, OPT_IN_TRUE);
+
+        assertThat(eligibleAccount.getAccount()).isEqualTo(ACCOUNT_NULL);
+        assertThat(eligibleAccount.isOptIn()).isEqualTo(OPT_IN_TRUE);
+    }
+
+    /* Generates FastPairEligibleAccount. */
+    private static FastPairEligibleAccount genFastPairEligibleAccount(
+            Account account, boolean optIn) {
+        FastPairEligibleAccount.Builder builder = new FastPairEligibleAccount.Builder();
+        builder.setAccount(account);
+        builder.setOptIn(optIn);
+
+        return builder.build();
+    }
+}
diff --git a/nearby/tests/unit/src/android/nearby/PairStatusMetadataTest.java b/nearby/tests/unit/src/android/nearby/PairStatusMetadataTest.java
new file mode 100644
index 0000000..7bc6519
--- /dev/null
+++ b/nearby/tests/unit/src/android/nearby/PairStatusMetadataTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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 android.nearby;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+
+public class PairStatusMetadataTest {
+    private static final int UNKNOWN = 1000;
+    private static final int SUCCESS = 1001;
+    private static final int FAIL = 1002;
+    private static final int DISMISS = 1003;
+
+    @Test
+    public void statusToString() {
+        assertThat(PairStatusMetadata.statusToString(UNKNOWN)).isEqualTo("UNKNOWN");
+        assertThat(PairStatusMetadata.statusToString(SUCCESS)).isEqualTo("SUCCESS");
+        assertThat(PairStatusMetadata.statusToString(FAIL)).isEqualTo("FAIL");
+        assertThat(PairStatusMetadata.statusToString(DISMISS)).isEqualTo("DISMISS");
+    }
+
+    @Test
+    public void getStatus() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        assertThat(pairStatusMetadata.getStatus()).isEqualTo(1001);
+        pairStatusMetadata = new PairStatusMetadata(FAIL);
+        assertThat(pairStatusMetadata.getStatus()).isEqualTo(1002);
+        pairStatusMetadata = new PairStatusMetadata(DISMISS);
+        assertThat(pairStatusMetadata.getStatus()).isEqualTo(1003);
+        pairStatusMetadata = new PairStatusMetadata(UNKNOWN);
+        assertThat(pairStatusMetadata.getStatus()).isEqualTo(1000);
+    }
+
+    @Test
+    public void testToString() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        assertThat(pairStatusMetadata.toString())
+                .isEqualTo("PairStatusMetadata[ status=SUCCESS]");
+        pairStatusMetadata = new PairStatusMetadata(FAIL);
+        assertThat(pairStatusMetadata.toString())
+                .isEqualTo("PairStatusMetadata[ status=FAIL]");
+        pairStatusMetadata = new PairStatusMetadata(DISMISS);
+        assertThat(pairStatusMetadata.toString())
+                .isEqualTo("PairStatusMetadata[ status=DISMISS]");
+        pairStatusMetadata = new PairStatusMetadata(UNKNOWN);
+        assertThat(pairStatusMetadata.toString())
+                .isEqualTo("PairStatusMetadata[ status=UNKNOWN]");
+    }
+
+    @Test
+    public void testEquals() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        PairStatusMetadata pairStatusMetadata1 = new PairStatusMetadata(SUCCESS);
+        PairStatusMetadata pairStatusMetadata2 = new PairStatusMetadata(UNKNOWN);
+        assertThat(pairStatusMetadata.equals(pairStatusMetadata1)).isTrue();
+        assertThat(pairStatusMetadata.equals(pairStatusMetadata2)).isFalse();
+        assertThat(pairStatusMetadata.hashCode()).isEqualTo(pairStatusMetadata1.hashCode());
+    }
+
+    @Test
+    public void testParcelable() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        Parcel dest = Parcel.obtain();
+        pairStatusMetadata.writeToParcel(dest, 0);
+        dest.setDataPosition(0);
+        PairStatusMetadata comparStatusMetadata =
+                PairStatusMetadata.CREATOR.createFromParcel(dest);
+        assertThat(pairStatusMetadata.equals(comparStatusMetadata)).isTrue();
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        PairStatusMetadata[] pairStatusMetadatas = PairStatusMetadata.CREATOR.newArray(2);
+        assertThat(pairStatusMetadatas.length).isEqualTo(2);
+    }
+
+    @Test
+    public void describeContents() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        assertThat(pairStatusMetadata.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void  getStability() {
+        PairStatusMetadata pairStatusMetadata = new PairStatusMetadata(SUCCESS);
+        assertThat(pairStatusMetadata.getStability()).isEqualTo(0);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleFilterTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleFilterTest.java
index 1d3653b..c4a9729 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleFilterTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleFilterTest.java
@@ -20,7 +20,11 @@
 
 import static org.junit.Assert.fail;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothAssignedNumbers;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.ScanFilter;
+import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.util.SparseArray;
 
@@ -44,6 +48,83 @@
     public static final ParcelUuid EDDYSTONE_SERVICE_DATA_PARCELUUID =
             ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB");
 
+    private final BleFilter mEddystoneFilter = createEddystoneFilter();
+    private final BleFilter mEddystoneUidFilter = createEddystoneUidFilter();
+    private final BleFilter mEddystoneUrlFilter = createEddystoneUrlFilter();
+    private final BleFilter mEddystoneEidFilter = createEddystoneEidFilter();
+    private final BleFilter mIBeaconWithoutUuidFilter = createIBeaconWithoutUuidFilter();
+    private final BleFilter mIBeaconWithUuidFilter = createIBeaconWithUuidFilter();
+    private final BleFilter mChromecastFilter =
+            new BleFilter.Builder().setServiceUuid(
+                    new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")))
+                    .build();
+    private final BleFilter mEddystoneWithDeviceNameFilter =
+            new BleFilter.Builder()
+                    .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID)
+                    .setDeviceName("BERT")
+                    .build();
+    private final BleFilter mEddystoneWithDeviceAddressFilter =
+            new BleFilter.Builder()
+                    .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID)
+                    .setDeviceAddress("00:11:22:33:AA:BB")
+                    .build();
+    private final BleFilter mServiceUuidWithMaskFilter1 =
+            new BleFilter.Builder()
+                    .setServiceUuid(
+                            new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")),
+                            new ParcelUuid(UUID.fromString("0000000-0000-000-FFFF-FFFFFFFFFFFF")))
+                    .build();
+    private final BleFilter mServiceUuidWithMaskFilter2 =
+            new BleFilter.Builder()
+                    .setServiceUuid(
+                            new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")),
+                            new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF")))
+                    .build();
+
+    private final BleFilter mSmartSetupFilter =
+            new BleFilter.Builder()
+                    .setManufacturerData(
+                            BluetoothAssignedNumbers.GOOGLE,
+                            new byte[] {0x00, 0x10},
+                            new byte[] {0x00, (byte) 0xFF})
+                    .build();
+    private final BleFilter mWearFilter =
+            new BleFilter.Builder()
+                    .setManufacturerData(
+                            BluetoothAssignedNumbers.GOOGLE,
+                            new byte[] {0x00, 0x00, 0x00},
+                            new byte[] {0x00, 0x00, (byte) 0xFF})
+                    .build();
+    private final BleFilter mFakeSmartSetupSubsetFilter =
+            new BleFilter.Builder()
+                    .setManufacturerData(
+                            BluetoothAssignedNumbers.GOOGLE,
+                            new byte[] {0x00, 0x10, 0x50},
+                            new byte[] {0x00, (byte) 0xFF, (byte) 0xFF})
+                    .build();
+    private final BleFilter mFakeSmartSetupNotSubsetFilter =
+            new BleFilter.Builder()
+                    .setManufacturerData(
+                            BluetoothAssignedNumbers.GOOGLE,
+                            new byte[] {0x00, 0x10, 0x50},
+                            new byte[] {0x00, (byte) 0x00, (byte) 0xFF})
+                    .build();
+
+    private final BleFilter mFakeFilter1 =
+            new BleFilter.Builder()
+                    .setServiceData(
+                            ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
+                            new byte[] {0x51, 0x64},
+                            new byte[] {0x00, (byte) 0xFF})
+                    .build();
+    private final BleFilter mFakeFilter2 =
+            new BleFilter.Builder()
+                    .setServiceData(
+                            ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
+                            new byte[] {0x51, 0x64, 0x34},
+                            new byte[] {0x00, (byte) 0xFF, (byte) 0xFF})
+                    .build();
+
     private ParcelUuid mServiceDataUuid;
     private BleSighting mBleSighting;
     private BleFilter.Builder mFilterBuilder;
@@ -229,6 +310,16 @@
     }
 
     @Test
+    public void serviceDataUuidNotInBleRecord() {
+        byte[] bleRecord = FastPairTestData.eir_1;
+        byte[] serviceData = {(byte) 0xe0, (byte) 0x00};
+
+        // Verify Service Data with 2-byte UUID, no data, and NOT in scan record
+        BleFilter filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData).build();
+        assertThat(matches(filter, null, 0, bleRecord)).isFalse();
+    }
+
+    @Test
     public void serviceDataMask() {
         byte[] bleRecord = FastPairTestData.sd1;
         BleFilter filter;
@@ -263,6 +354,18 @@
         mFilterBuilder.setServiceData(mServiceDataUuid, serviceData, mask).build();
     }
 
+    @Test
+    public void serviceDataMaskNotInBleRecord() {
+        byte[] bleRecord = FastPairTestData.eir_1;
+        BleFilter filter;
+
+        // Verify matching partial manufacturer with data and mask
+        byte[] serviceData1 = {(byte) 0xe0, (byte) 0x00, (byte) 0x15};
+        byte[] mask1 = {(byte) 0xff, (byte) 0xff, (byte) 0xff};
+        filter = mFilterBuilder.setServiceData(mServiceDataUuid, serviceData1, mask1).build();
+        assertThat(matches(filter, null, 0, bleRecord)).isFalse();
+    }
+
 
     @Test
     public void deviceNameTest() {
@@ -280,12 +383,241 @@
         assertThat(matches(filter, null, 0, bleRecord)).isFalse();
     }
 
+    @Test
+    public void deviceNameNotInBleRecord() {
+        // Verify the name filter does not match
+        byte[] bleRecord = FastPairTestData.eir_1;
+        BleFilter filter = mFilterBuilder.setDeviceName("Pedometer").build();
+        assertThat(matches(filter, null, 0, bleRecord)).isFalse();
+    }
+
+    @Test
+    public void serviceUuid() {
+        byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid;
+        ParcelUuid uuid = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB");
+
+        BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build();
+        assertMatches(filter, null, 0, bleRecord);
+    }
+
+    @Test
+    public void serviceUuidNoMatch() {
+        // Verify the name filter does not match
+        byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid;
+        ParcelUuid uuid = ParcelUuid.fromString("00001804-0000-1000-8000-000000000000");
+
+        BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build();
+        assertThat(matches(filter, null, 0, bleRecord)).isFalse();
+    }
+
+    @Test
+    public void serviceUuidNotInBleRecord() {
+        // Verify the name filter does not match
+        byte[] bleRecord = FastPairTestData.eir_1;
+        ParcelUuid uuid = ParcelUuid.fromString("00001804-0000-1000-8000-000000000000");
+
+        BleFilter filter = mFilterBuilder.setServiceUuid(uuid).build();
+        assertThat(matches(filter, null, 0, bleRecord)).isFalse();
+    }
+
+    @Test
+    public void serviceUuidMask() {
+        byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid;
+        ParcelUuid uuid = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB");
+        ParcelUuid mask = ParcelUuid.fromString("00000000-0000-0000-0000-FFFFFFFFFFFF");
+        BleFilter filter = mFilterBuilder.setServiceUuid(uuid, mask).build();
+        assertMatches(filter, null, 0, bleRecord);
+    }
+
+
+    @Test
+    public void macAddress() {
+        byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid;
+        String macAddress = "00:11:22:33:AA:BB";
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        BluetoothDevice device = adapter.getRemoteDevice(macAddress);
+        BleFilter filter = mFilterBuilder.setDeviceAddress(macAddress).build();
+        assertMatches(filter, device, 0, bleRecord);
+    }
+
+    @Test
+    public void macAddressNoMatch() {
+        byte[] bleRecord = FastPairTestData.eddystone_header_and_uuid;
+        String macAddress = "00:11:22:33:AA:00";
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        BluetoothDevice device = adapter.getRemoteDevice("00:11:22:33:AA:BB");
+        BleFilter filter = mFilterBuilder.setDeviceAddress(macAddress).build();
+        assertThat(matches(filter, device, 0, bleRecord)).isFalse();
+    }
+
+    @Test
+    public void eddystoneIsSuperset() {
+        // Verify eddystone subtypes pass.
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneFilter)).isTrue();
+        assertThat(mEddystoneUidFilter.isSuperset(mEddystoneUidFilter)).isTrue();
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneUidFilter)).isTrue();
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneEidFilter)).isTrue();
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneUrlFilter)).isTrue();
+
+        // Non-eddystone beacon filters should never be supersets.
+        assertThat(mEddystoneFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse();
+        assertThat(mEddystoneFilter.isSuperset(mWearFilter)).isFalse();
+        assertThat(mEddystoneFilter.isSuperset(mSmartSetupFilter)).isFalse();
+        assertThat(mEddystoneFilter.isSuperset(mChromecastFilter)).isFalse();
+        assertThat(mEddystoneFilter.isSuperset(mFakeFilter1)).isFalse();
+        assertThat(mEddystoneFilter.isSuperset(mFakeFilter2)).isFalse();
+
+        assertThat(mEddystoneUidFilter.isSuperset(mWearFilter)).isFalse();
+        assertThat(mEddystoneUidFilter.isSuperset(mSmartSetupFilter)).isFalse();
+        assertThat(mEddystoneUidFilter.isSuperset(mChromecastFilter)).isFalse();
+        assertThat(mEddystoneUidFilter.isSuperset(mFakeFilter1)).isFalse();
+        assertThat(mEddystoneUidFilter.isSuperset(mFakeFilter2)).isFalse();
+    }
+
+    @Test
+    public void iBeaconIsSuperset() {
+        // Verify that an iBeacon filter is a superset of itself and any filters that specify UUIDs.
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mIBeaconWithoutUuidFilter)).isTrue();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mIBeaconWithUuidFilter)).isTrue();
+
+        // Non-iBeacon filters should never be supersets.
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneEidFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneUrlFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mEddystoneUidFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mWearFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mSmartSetupFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mChromecastFilter)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mFakeFilter1)).isFalse();
+        assertThat(mIBeaconWithoutUuidFilter.isSuperset(mFakeFilter2)).isFalse();
+    }
+
+    @Test
+    public void mixedFilterIsSuperset() {
+        // Compare service data vs manufacturer data filters to verify we detect supersets
+        // correctly in filters that aren't for iBeacon and Eddystone.
+        assertThat(mWearFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mIBeaconWithoutUuidFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mEddystoneFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mEddystoneFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mEddystoneFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mEddystoneUidFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mEddystoneUidFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mEddystoneUidFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mEddystoneEidFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mEddystoneEidFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mEddystoneEidFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mEddystoneUrlFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mEddystoneUrlFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mEddystoneUrlFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mIBeaconWithUuidFilter)).isFalse();
+
+        assertThat(mWearFilter.isSuperset(mChromecastFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mChromecastFilter)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mWearFilter)).isFalse();
+        assertThat(mChromecastFilter.isSuperset(mWearFilter)).isFalse();
+
+        assertThat(mFakeFilter1.isSuperset(mFakeFilter2)).isTrue();
+        assertThat(mFakeFilter2.isSuperset(mFakeFilter1)).isFalse();
+        assertThat(mSmartSetupFilter.isSuperset(mFakeSmartSetupSubsetFilter)).isTrue();
+        assertThat(mSmartSetupFilter.isSuperset(mFakeSmartSetupNotSubsetFilter)).isFalse();
+
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneWithDeviceNameFilter)).isTrue();
+        assertThat(mEddystoneFilter.isSuperset(mEddystoneWithDeviceAddressFilter)).isTrue();
+        assertThat(mEddystoneWithDeviceAddressFilter.isSuperset(mEddystoneFilter)).isFalse();
+
+        assertThat(mChromecastFilter.isSuperset(mServiceUuidWithMaskFilter1)).isTrue();
+        assertThat(mServiceUuidWithMaskFilter2.isSuperset(mServiceUuidWithMaskFilter1)).isFalse();
+        assertThat(mServiceUuidWithMaskFilter1.isSuperset(mServiceUuidWithMaskFilter2)).isTrue();
+        assertThat(mEddystoneFilter.isSuperset(mServiceUuidWithMaskFilter1)).isFalse();
+    }
+
+    @Test
+    public void toOsFilter_getTheSameFilterParameter() {
+        BleFilter nearbyFilter = createTestFilter();
+        ScanFilter osFilter = nearbyFilter.toOsFilter();
+        assertFilterValuesEqual(nearbyFilter, osFilter);
+    }
+
+    @Test
+    public void describeContents() {
+        BleFilter nearbyFilter = createTestFilter();
+        assertThat(nearbyFilter.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void testHashCode() {
+        BleFilter nearbyFilter = createTestFilter();
+        BleFilter compareFilter = new BleFilter("BERT", "00:11:22:33:AA:BB",
+                new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")),
+                new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF")),
+                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
+                new byte[] {0x51, 0x64}, new byte[] {0x00, (byte) 0xFF},
+                BluetoothAssignedNumbers.GOOGLE, new byte[] {0x00, 0x10},
+                new byte[] {0x00, (byte) 0xFF});
+        assertThat(nearbyFilter.hashCode()).isEqualTo(compareFilter.hashCode());
+    }
+
+    @Test
+    public void testToString() {
+        BleFilter nearbyFilter = createTestFilter();
+        assertThat(nearbyFilter.toString()).isEqualTo("BleFilter [deviceName=BERT,"
+                + " deviceAddress=00:11:22:33:AA:BB, uuid=0000fea0-0000-1000-8000-00805f9b34fb,"
+                + " uuidMask=0fffffff-ffff-0fff-ffff-ffffffffffff,"
+                + " serviceDataUuid=0000110b-0000-1000-8000-00805f9b34fb,"
+                + " serviceData=[81, 100], serviceDataMask=[0, -1],"
+                + " manufacturerId=224, manufacturerData=[0, 16], manufacturerDataMask=[0, -1]]");
+    }
+
+    @Test
+    public void testParcel() {
+        BleFilter nearbyFilter = createTestFilter();
+        Parcel parcel = Parcel.obtain();
+        nearbyFilter.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        BleFilter compareFilter = BleFilter.CREATOR.createFromParcel(
+                parcel);
+        parcel.recycle();
+        assertThat(compareFilter.getDeviceName()).isEqualTo("BERT");
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        BleFilter[] nearbyFilters  = BleFilter.CREATOR.newArray(2);
+        assertThat(nearbyFilters.length).isEqualTo(2);
+    }
+
     private static boolean matches(
             BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecord) {
         return filter.matches(new BleSighting(device,
                 bleRecord, rssi, 0 /* timestampNanos */));
     }
 
+    private static void assertFilterValuesEqual(BleFilter nearbyFilter, ScanFilter osFilter) {
+        assertThat(osFilter.getDeviceAddress()).isEqualTo(nearbyFilter.getDeviceAddress());
+        assertThat(osFilter.getDeviceName()).isEqualTo(nearbyFilter.getDeviceName());
+
+        assertThat(osFilter.getManufacturerData()).isEqualTo(nearbyFilter.getManufacturerData());
+        assertThat(osFilter.getManufacturerDataMask())
+                .isEqualTo(nearbyFilter.getManufacturerDataMask());
+        assertThat(osFilter.getManufacturerId()).isEqualTo(nearbyFilter.getManufacturerId());
+
+        assertThat(osFilter.getServiceData()).isEqualTo(nearbyFilter.getServiceData());
+        assertThat(osFilter.getServiceDataMask()).isEqualTo(nearbyFilter.getServiceDataMask());
+        assertThat(osFilter.getServiceDataUuid()).isEqualTo(nearbyFilter.getServiceDataUuid());
+
+        assertThat(osFilter.getServiceUuid()).isEqualTo(nearbyFilter.getServiceUuid());
+        assertThat(osFilter.getServiceUuidMask()).isEqualTo(nearbyFilter.getServiceUuidMask());
+    }
 
     private static void assertMatches(
             BleFilter filter, BluetoothDevice device, int rssi, byte[] bleRecordBytes) {
@@ -325,10 +657,10 @@
 
         // UUID match.
         if (filter.getServiceUuid() != null
-                && !matchesServiceUuids(filter.getServiceUuid(), filter.getServiceUuidMask(),
-                bleRecord.getServiceUuids())) {
-            fail("The filter specifies a service UUID but it doesn't match "
-                    + "what's in the scan record");
+                && !matchesServiceUuids(filter.getServiceUuid(),
+                filter.getServiceUuidMask(), bleRecord.getServiceUuids())) {
+            fail("The filter specifies a service UUID "
+                    + "but it doesn't match what's in the scan record");
         }
 
         // Service data match
@@ -401,6 +733,95 @@
         }
     }
 
+    private static String byteString(Map<ParcelUuid, byte[]> bytesMap) {
+        StringBuilder builder = new StringBuilder();
+        for (Map.Entry<ParcelUuid, byte[]> entry : bytesMap.entrySet()) {
+            builder.append(builder.toString().isEmpty() ? "  " : "\n  ");
+            builder.append(entry.getKey().toString());
+            builder.append(" --> ");
+            builder.append(byteString(entry.getValue()));
+        }
+        return builder.toString();
+    }
+
+    private static String byteString(SparseArray<byte[]> bytesArray) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < bytesArray.size(); i++) {
+            builder.append(builder.toString().isEmpty() ? "  " : "\n  ");
+            builder.append(byteString(bytesArray.valueAt(i)));
+        }
+        return builder.toString();
+    }
+
+    private static BleFilter createTestFilter() {
+        BleFilter.Builder builder = new BleFilter.Builder();
+        builder
+                .setServiceUuid(
+                        new ParcelUuid(UUID.fromString("0000FEA0-0000-1000-8000-00805F9B34FB")),
+                        new ParcelUuid(UUID.fromString("FFFFFFF-FFFF-FFF-FFFF-FFFFFFFFFFFF")))
+                .setDeviceAddress("00:11:22:33:AA:BB")
+                .setDeviceName("BERT")
+                .setManufacturerData(
+                        BluetoothAssignedNumbers.GOOGLE,
+                        new byte[] {0x00, 0x10},
+                        new byte[] {0x00, (byte) 0xFF})
+                .setServiceData(
+                        ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
+                        new byte[] {0x51, 0x64},
+                        new byte[] {0x00, (byte) 0xFF});
+        return builder.build();
+    }
+
+    // ref to beacon.decode.BeaconFilterBuilder.eddystoneFilter()
+    private static BleFilter createEddystoneFilter() {
+        return new BleFilter.Builder().setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID).build();
+    }
+    // ref to beacon.decode.BeaconFilterBuilder.eddystoneUidFilter()
+    private static BleFilter createEddystoneUidFilter() {
+        return new BleFilter.Builder()
+                .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID)
+                .setServiceData(
+                        EDDYSTONE_SERVICE_DATA_PARCELUUID, new byte[] {(short) 0x00},
+                        new byte[] {(byte) 0xf0})
+                .build();
+    }
+
+    // ref to beacon.decode.BeaconFilterBuilder.eddystoneUrlFilter()
+    private static BleFilter createEddystoneUrlFilter() {
+        return new BleFilter.Builder()
+                .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID)
+                .setServiceData(
+                        EDDYSTONE_SERVICE_DATA_PARCELUUID,
+                        new byte[] {(short) 0x10}, new byte[] {(byte) 0xf0})
+                .build();
+    }
+
+    // ref to beacon.decode.BeaconFilterBuilder.eddystoneEidFilter()
+    private static BleFilter createEddystoneEidFilter() {
+        return new BleFilter.Builder()
+                .setServiceUuid(EDDYSTONE_SERVICE_DATA_PARCELUUID)
+                .setServiceData(
+                        EDDYSTONE_SERVICE_DATA_PARCELUUID,
+                        new byte[] {(short) 0x30}, new byte[] {(byte) 0xf0})
+                .build();
+    }
+
+    // ref to beacon.decode.BeaconFilterBuilder.iBeaconWithoutUuidFilter()
+    private static BleFilter createIBeaconWithoutUuidFilter() {
+        byte[] data = {(byte) 0x02, (byte) 0x15};
+        byte[] mask = {(byte) 0xff, (byte) 0xff};
+
+        return new BleFilter.Builder().setManufacturerData((short) 0x004C, data, mask).build();
+    }
+
+    // ref to beacon.decode.BeaconFilterBuilder.iBeaconWithUuidFilter()
+    private static BleFilter createIBeaconWithUuidFilter() {
+        byte[] data = getFilterData(ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"));
+        byte[] mask = getFilterMask(ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"));
+
+        return new BleFilter.Builder().setManufacturerData((short) 0x004C, data, mask).build();
+    }
+
     // Ref to beacon.decode.AppleBeaconDecoder.getFilterData
     private static byte[] getFilterData(ParcelUuid uuid) {
         byte[] data = new byte[18];
@@ -418,6 +839,20 @@
         return data;
     }
 
+    // Ref to beacon.decode.AppleBeaconDecoder.getFilterMask
+    private static byte[] getFilterMask(ParcelUuid uuid) {
+        byte[] mask = new byte[18];
+        mask[0] = (byte) 0xff;
+        mask[1] = (byte) 0xff;
+        // Check if UUID is needed in data
+        if (uuid != null) {
+            for (int i = 0; i < 16; i++) {
+                mask[i + 2] = (byte) 0xff;
+            }
+        }
+        return mask;
+    }
+
     // Ref to beacon.decode.AppleBeaconDecoder.uuidToByteArray
     private static byte[] uuidToByteArray(ParcelUuid uuid) {
         ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
@@ -453,24 +888,4 @@
         return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits())
                 == (data.getMostSignificantBits() & mask.getMostSignificantBits()));
     }
-
-    private static String byteString(Map<ParcelUuid, byte[]> bytesMap) {
-        StringBuilder builder = new StringBuilder();
-        for (Map.Entry<ParcelUuid, byte[]> entry : bytesMap.entrySet()) {
-            builder.append(builder.toString().isEmpty() ? "  " : "\n  ");
-            builder.append(entry.getKey().toString());
-            builder.append(" --> ");
-            builder.append(byteString(entry.getValue()));
-        }
-        return builder.toString();
-    }
-
-    private static String byteString(SparseArray<byte[]> bytesArray) {
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < bytesArray.size(); i++) {
-            builder.append(builder.toString().isEmpty() ? "  " : "\n  ");
-            builder.append(byteString(bytesArray.valueAt(i)));
-        }
-        return builder.toString();
-    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleRecordTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleRecordTest.java
index 5da98e2..3f9a259 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleRecordTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleRecordTest.java
@@ -34,6 +34,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.ParcelUuid;
+import android.util.SparseArray;
+
 import androidx.test.filters.SdkSuppress;
 
 import org.junit.Test;
@@ -238,7 +241,6 @@
         BleRecord record = BleRecord.parseFromBytes(BEACON);
         BleRecord record2 = BleRecord.parseFromBytes(SAME_BEACON);
 
-
         assertThat(record).isEqualTo(record2);
 
         // Different items.
@@ -246,5 +248,48 @@
         assertThat(record).isNotEqualTo(record2);
         assertThat(record.hashCode()).isNotEqualTo(record2.hashCode());
     }
+
+    @Test
+    public void testFields() {
+        BleRecord record = BleRecord.parseFromBytes(BEACON);
+        assertThat(byteString(record.getManufacturerSpecificData()))
+                .isEqualTo("  0215F7826DA64FA24E988024BC5B71E0893E44D02522B3");
+        assertThat(
+                byteString(record.getServiceData(
+                        ParcelUuid.fromString("000000E0-0000-1000-8000-00805F9B34FB"))))
+                .isEqualTo("[null]");
+        assertThat(record.getTxPowerLevel()).isEqualTo(-12);
+        assertThat(record.toString()).isEqualTo(
+                "BleRecord [advertiseFlags=6, serviceUuids=[], "
+                        + "manufacturerSpecificData={76=[2, 21, -9, -126, 109, -90, 79, -94, 78,"
+                        + " -104, -128, 36, -68, 91, 113, -32, -119, 62, 68, -48, 37, 34, -77]},"
+                        + " serviceData={0000d00d-0000-1000-8000-00805f9b34fb"
+                        + "=[116, 109, 77, 107, 50, 54, 100]},"
+                        + " txPowerLevel=-12, deviceName=Kontakt]");
+    }
+
+    private static String byteString(SparseArray<byte[]> bytesArray) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < bytesArray.size(); i++) {
+            builder.append(builder.toString().isEmpty() ? "  " : "\n  ");
+            builder.append(byteString(bytesArray.valueAt(i)));
+        }
+        return builder.toString();
+    }
+
+    private static String byteString(byte[] bytes) {
+        if (bytes == null) {
+            return "[null]";
+        } else {
+            final char[] hexArray = "0123456789ABCDEF".toCharArray();
+            char[] hexChars = new char[bytes.length * 2];
+            for (int i = 0; i < bytes.length; i++) {
+                int v = bytes[i] & 0xFF;
+                hexChars[i * 2] = hexArray[v >>> 4];
+                hexChars[i * 2 + 1] = hexArray[v & 0x0F];
+            }
+            return new String(hexChars);
+        }
+    }
 }
 
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleSightingTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleSightingTest.java
new file mode 100644
index 0000000..d259851
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/BleSightingTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.ble;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.os.Parcel;
+
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+/** Test for Bluetooth LE {@link BleSighting}. */
+public class BleSightingTest {
+    private static final String DEVICE_NAME = "device1";
+    private static final String OTHER_DEVICE_NAME = "device2";
+    private static final long TIME_EPOCH_MILLIS = 123456;
+    private static final long OTHER_TIME_EPOCH_MILLIS = 456789;
+    private static final int RSSI = 1;
+    private static final int OTHER_RSSI = 2;
+
+    private final BluetoothDevice mBluetoothDevice1 =
+            BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:44:55");
+    private final BluetoothDevice mBluetoothDevice2 =
+            BluetoothAdapter.getDefaultAdapter().getRemoteDevice("AA:BB:CC:DD:EE:FF");
+
+
+    @Test
+    public void testEquals() {
+        BleSighting sighting =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        BleSighting sighting2 =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        assertThat(sighting.equals(sighting2)).isTrue();
+        assertThat(sighting2.equals(sighting)).isTrue();
+        assertThat(sighting.hashCode()).isEqualTo(sighting2.hashCode());
+
+        // Transitive property.
+        BleSighting sighting3 =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        assertThat(sighting2.equals(sighting3)).isTrue();
+        assertThat(sighting.equals(sighting3)).isTrue();
+
+        // Set different values for each field, one at a time.
+        sighting2 = buildBleSighting(mBluetoothDevice2, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        assertSightingsNotEquals(sighting, sighting2);
+
+        sighting2 = buildBleSighting(mBluetoothDevice1, OTHER_DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        assertSightingsNotEquals(sighting, sighting2);
+
+        sighting2 = buildBleSighting(mBluetoothDevice1, DEVICE_NAME, OTHER_TIME_EPOCH_MILLIS, RSSI);
+        assertSightingsNotEquals(sighting, sighting2);
+
+        sighting2 = buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, OTHER_RSSI);
+        assertSightingsNotEquals(sighting, sighting2);
+    }
+
+    @Test
+    public void getNormalizedRSSI_usingNearbyRssiOffset_getCorrectValue() {
+        BleSighting sighting =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+
+        int defaultRssiOffset = 3;
+        assertThat(sighting.getNormalizedRSSI()).isEqualTo(RSSI + defaultRssiOffset);
+    }
+
+    @Test
+    public void testFields() {
+        BleSighting sighting =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        assertThat(byteString(sighting.getBleRecordBytes()))
+                .isEqualTo("080964657669636531");
+        assertThat(sighting.getRssi()).isEqualTo(RSSI);
+        assertThat(sighting.getTimestampMillis()).isEqualTo(TIME_EPOCH_MILLIS);
+        assertThat(sighting.getTimestampNanos())
+                .isEqualTo(TimeUnit.MILLISECONDS.toNanos(TIME_EPOCH_MILLIS));
+        assertThat(sighting.toString()).isEqualTo(
+                "BleSighting{device=00:11:22:33:44:55,"
+                        + " bleRecord=BleRecord [advertiseFlags=-1,"
+                        + " serviceUuids=[],"
+                        + " manufacturerSpecificData={}, serviceData={},"
+                        + " txPowerLevel=-2147483648,"
+                        + " deviceName=device1],"
+                        + " rssi=1,"
+                        + " timestampNanos=123456000000}");
+    }
+
+    @Test
+    public void testParcelable() {
+        BleSighting sighting =
+                buildBleSighting(mBluetoothDevice1, DEVICE_NAME, TIME_EPOCH_MILLIS, RSSI);
+        Parcel dest = Parcel.obtain();
+        sighting.writeToParcel(dest, 0);
+        dest.setDataPosition(0);
+        assertThat(sighting.getRssi()).isEqualTo(RSSI);
+    }
+
+    @Test
+    public void testCreatorNewArray() {
+        BleSighting[]  sightings =
+                BleSighting.CREATOR.newArray(2);
+        assertThat(sightings.length).isEqualTo(2);
+    }
+
+    private static String byteString(byte[] bytes) {
+        if (bytes == null) {
+            return "[null]";
+        } else {
+            final char[] hexArray = "0123456789ABCDEF".toCharArray();
+            char[] hexChars = new char[bytes.length * 2];
+            for (int i = 0; i < bytes.length; i++) {
+                int v = bytes[i] & 0xFF;
+                hexChars[i * 2] = hexArray[v >>> 4];
+                hexChars[i * 2 + 1] = hexArray[v & 0x0F];
+            }
+            return new String(hexChars);
+        }
+    }
+
+        /** Builds a BleSighting instance which will correctly match filters by device name. */
+    private static BleSighting buildBleSighting(
+            BluetoothDevice bluetoothDevice, String deviceName, long timeEpochMillis, int rssi) {
+        byte[] nameBytes = deviceName.getBytes(UTF_8);
+        byte[] bleRecordBytes = new byte[nameBytes.length + 2];
+        bleRecordBytes[0] = (byte) (nameBytes.length + 1);
+        bleRecordBytes[1] = 0x09; // Value of private BleRecord.DATA_TYPE_LOCAL_NAME_COMPLETE;
+        System.arraycopy(nameBytes, 0, bleRecordBytes, 2, nameBytes.length);
+
+        return new BleSighting(bluetoothDevice, bleRecordBytes,
+                rssi, TimeUnit.MILLISECONDS.toNanos(timeEpochMillis));
+    }
+
+    private static void assertSightingsNotEquals(BleSighting sighting1, BleSighting sighting2) {
+        assertThat(sighting1.equals(sighting2)).isFalse();
+        assertThat(sighting1.hashCode()).isNotEqualTo(sighting2.hashCode());
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/BeaconDecoderTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/BeaconDecoderTest.java
new file mode 100644
index 0000000..9a9181d
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/BeaconDecoderTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.ble.decode;
+
+import static com.android.server.nearby.common.ble.BleRecord.parseFromBytes;
+import static com.android.server.nearby.common.ble.testing.FastPairTestData.getFastPairRecord;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class BeaconDecoderTest {
+    private BeaconDecoder mBeaconDecoder = new FastPairDecoder();;
+
+    @Test
+    public void testFields() {
+        assertThat(mBeaconDecoder.getTelemetry(parseFromBytes(getFastPairRecord()))).isNull();
+        assertThat(mBeaconDecoder.getUrl(parseFromBytes(getFastPairRecord()))).isNull();
+        assertThat(mBeaconDecoder.supportsBeaconIdAndTxPower(parseFromBytes(getFastPairRecord())))
+                .isTrue();
+        assertThat(mBeaconDecoder.supportsTxPower()).isTrue();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/FastPairDecoderTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/FastPairDecoderTest.java
index 1ad04f8..6552699 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/FastPairDecoderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/decode/FastPairDecoderTest.java
@@ -17,15 +17,23 @@
 package com.android.server.nearby.common.ble.decode;
 
 import static com.android.server.nearby.common.ble.BleRecord.parseFromBytes;
+import static com.android.server.nearby.common.ble.testing.FastPairTestData.DEVICE_ADDRESS;
 import static com.android.server.nearby.common.ble.testing.FastPairTestData.FAST_PAIR_MODEL_ID;
+import static com.android.server.nearby.common.ble.testing.FastPairTestData.FAST_PAIR_SHARED_ACCOUNT_KEY_RECORD;
+import static com.android.server.nearby.common.ble.testing.FastPairTestData.RSSI;
 import static com.android.server.nearby.common.ble.testing.FastPairTestData.getFastPairRecord;
 import static com.android.server.nearby.common.ble.testing.FastPairTestData.newFastPairRecord;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.server.nearby.common.ble.BleRecord;
+import com.android.server.nearby.common.ble.BleSighting;
+import com.android.server.nearby.common.ble.testing.FastPairTestData;
 import com.android.server.nearby.util.Hex;
 
 import com.google.common.primitives.Bytes;
@@ -34,12 +42,13 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 public class FastPairDecoderTest {
     private static final String LONG_MODEL_ID = "1122334455667788";
-    private final FastPairDecoder mDecoder = new FastPairDecoder();
     // Bits 3-6 are model ID length bits = 0b1000 = 8
     private static final byte LONG_MODEL_ID_HEADER = 0b00010000;
     private static final String PADDED_LONG_MODEL_ID = "00001111";
@@ -49,74 +58,239 @@
     private static final byte MODEL_ID_HEADER = 0b00000110;
     private static final String MODEL_ID = "112233";
     private static final byte BLOOM_FILTER_HEADER = 0b01100000;
+    private static final byte BLOOM_FILTER_NO_NOTIFICATION_HEADER = 0b01100010;
     private static final String BLOOM_FILTER = "112233445566";
+    private static final byte LONG_BLOOM_FILTER_HEADER = (byte) 0b10100000;
+    private static final String LONG_BLOOM_FILTER = "00112233445566778899";
     private static final byte BLOOM_FILTER_SALT_HEADER = 0b00010001;
     private static final String BLOOM_FILTER_SALT = "01";
+    private static final byte BATTERY_HEADER = 0b00110011;
+    private static final byte BATTERY_NO_NOTIFICATION_HEADER = 0b00110100;
+    private static final String BATTERY = "01048F";
     private static final byte RANDOM_RESOLVABLE_DATA_HEADER = 0b01000110;
     private static final String RANDOM_RESOLVABLE_DATA = "11223344";
-    private static final byte BLOOM_FILTER_NO_NOTIFICATION_HEADER = 0b01100010;
 
+    private final FastPairDecoder mDecoder = new FastPairDecoder();
+    private final BluetoothDevice mBluetoothDevice =
+            BluetoothAdapter.getDefaultAdapter().getRemoteDevice(DEVICE_ADDRESS);
+
+    @Test
+    public void filter() {
+        assertThat(FastPairDecoder.FILTER.matches(bleSighting(getFastPairRecord()))).isTrue();
+        assertThat(FastPairDecoder.FILTER.matches(bleSighting(FAST_PAIR_SHARED_ACCOUNT_KEY_RECORD)))
+                .isTrue();
+
+        // Any ID is a valid frame.
+        assertThat(FastPairDecoder.FILTER.matches(
+                bleSighting(newFastPairRecord(Hex.stringToBytes("000001"))))).isTrue();
+        assertThat(FastPairDecoder.FILTER.matches(
+                bleSighting(newFastPairRecord(Hex.stringToBytes("098FEC"))))).isTrue();
+        assertThat(FastPairDecoder.FILTER.matches(
+                bleSighting(FastPairTestData.newFastPairRecord(
+                        LONG_MODEL_ID_HEADER, Hex.stringToBytes(LONG_MODEL_ID))))).isTrue();
+    }
 
     @Test
     public void getModelId() {
         assertThat(mDecoder.getBeaconIdBytes(parseFromBytes(getFastPairRecord())))
                 .isEqualTo(FAST_PAIR_MODEL_ID);
-        FastPairServiceData fastPairServiceData1 =
-                new FastPairServiceData(LONG_MODEL_ID_HEADER,
-                        LONG_MODEL_ID);
-        assertThat(
-                mDecoder.getBeaconIdBytes(
-                        newBleRecord(fastPairServiceData1.createServiceData())))
-                .isEqualTo(Hex.stringToBytes(LONG_MODEL_ID));
         FastPairServiceData fastPairServiceData =
-                new FastPairServiceData(PADDED_LONG_MODEL_ID_HEADER,
-                        PADDED_LONG_MODEL_ID);
+                new FastPairServiceData(LONG_MODEL_ID_HEADER, LONG_MODEL_ID);
         assertThat(
                 mDecoder.getBeaconIdBytes(
                         newBleRecord(fastPairServiceData.createServiceData())))
+                .isEqualTo(Hex.stringToBytes(LONG_MODEL_ID));
+
+        FastPairServiceData fastPairServiceData1 =
+                new FastPairServiceData(PADDED_LONG_MODEL_ID_HEADER, PADDED_LONG_MODEL_ID);
+        assertThat(
+                mDecoder.getBeaconIdBytes(
+                        newBleRecord(fastPairServiceData1.createServiceData())))
                 .isEqualTo(Hex.stringToBytes(TRIMMED_LONG_MODEL_ID));
     }
 
     @Test
-    public void getBloomFilter() {
-        FastPairServiceData fastPairServiceData = new FastPairServiceData(MODEL_ID_HEADER,
-                MODEL_ID);
-        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
-        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
-        assertThat(FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
-                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    public void getBeaconIdType() {
+        assertThat(mDecoder.getBeaconIdType()).isEqualTo(1);
     }
 
     @Test
-    public void getBloomFilter_smallModelId() {
-        FastPairServiceData fastPairServiceData = new FastPairServiceData(null, MODEL_ID);
-        assertThat(FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+    public void getCalibratedBeaconTxPower() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(LONG_MODEL_ID_HEADER, LONG_MODEL_ID);
+        assertThat(
+                mDecoder.getCalibratedBeaconTxPower(
+                        newBleRecord(fastPairServiceData.createServiceData())))
                 .isNull();
     }
 
     @Test
-    public void getBloomFilterSalt_modelIdAndMultipleExtraFields() {
-        FastPairServiceData fastPairServiceData = new FastPairServiceData(MODEL_ID_HEADER,
-                MODEL_ID);
-        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
-        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_SALT_HEADER);
-        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
-        fastPairServiceData.mExtraFields.add(BLOOM_FILTER_SALT);
+    public void getServiceDataArray() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(LONG_MODEL_ID_HEADER, LONG_MODEL_ID);
         assertThat(
-                FastPairDecoder.getBloomFilterSalt(fastPairServiceData.createServiceData()))
-                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER_SALT));
+                mDecoder.getServiceDataArray(
+                        newBleRecord(fastPairServiceData.createServiceData())))
+                .isEqualTo(Hex.stringToBytes("101122334455667788"));
     }
 
     @Test
-    public void getRandomResolvableData_whenContainConnectionState() {
-        FastPairServiceData fastPairServiceData = new FastPairServiceData(MODEL_ID_HEADER,
-                MODEL_ID);
-        fastPairServiceData.mExtraFieldHeaders.add(RANDOM_RESOLVABLE_DATA_HEADER);
-        fastPairServiceData.mExtraFields.add(RANDOM_RESOLVABLE_DATA);
+    public void hasBloomFilter() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(LONG_MODEL_ID_HEADER, LONG_MODEL_ID);
         assertThat(
-                FastPairDecoder.getRandomResolvableData(fastPairServiceData
-                                .createServiceData()))
-                .isEqualTo(Hex.stringToBytes(RANDOM_RESOLVABLE_DATA));
+                mDecoder.hasBloomFilter(
+                        newBleRecord(fastPairServiceData.createServiceData())))
+                .isFalse();
+    }
+
+    @Test
+    public void hasModelId_allCases() {
+        // One type of the format is just the 3-byte model ID. This format has no header byte (all 3
+        // service data bytes are the model ID in little endian).
+        assertThat(hasModelId("112233", mDecoder)).isTrue();
+
+        // If the model ID is shorter than 3 bytes, then return null.
+        assertThat(hasModelId("11", mDecoder)).isFalse();
+
+        // If the data is longer than 3 bytes,
+        // byte 0 must be 0bVVVLLLLR (version, ID length, reserved).
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00001000, "11223344");
+        assertThat(
+                FastPairDecoder.hasBeaconIdBytes(
+                        newBleRecord(fastPairServiceData.createServiceData()))).isTrue();
+
+        FastPairServiceData fastPairServiceData1 =
+                new FastPairServiceData((byte) 0b00001010, "1122334455");
+        assertThat(
+                FastPairDecoder.hasBeaconIdBytes(
+                        newBleRecord(fastPairServiceData1.createServiceData()))).isTrue();
+
+        // Length bits correct, but version bits != version 0 (only supported version).
+        FastPairServiceData fastPairServiceData2 =
+                new FastPairServiceData((byte) 0b00101000, "11223344");
+        assertThat(
+                FastPairDecoder.hasBeaconIdBytes(
+                        newBleRecord(fastPairServiceData2.createServiceData()))).isFalse();
+
+        // Version bits correct, but length bits incorrect (too big, too small).
+        FastPairServiceData fastPairServiceData3 =
+                new FastPairServiceData((byte) 0b00001010, "11223344");
+        assertThat(
+                FastPairDecoder.hasBeaconIdBytes(
+                        newBleRecord(fastPairServiceData3.createServiceData()))).isFalse();
+
+        FastPairServiceData fastPairServiceData4 =
+                new FastPairServiceData((byte) 0b00000010, "11223344");
+        assertThat(
+                FastPairDecoder.hasBeaconIdBytes(
+                        newBleRecord(fastPairServiceData4.createServiceData()))).isFalse();
+    }
+
+    @Test
+    public void getBatteryLevel() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        assertThat(
+                FastPairDecoder.getBatteryLevel(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBatteryLevel_notIncludedInPacket() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBatteryLevel(fastPairServiceData.createServiceData())).isNull();
+    }
+
+    @Test
+    public void getBatteryLevel_noModelId() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00000000, null);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        assertThat(
+                FastPairDecoder.getBatteryLevel(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBatteryLevel_multipelExtraField() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBatteryLevel(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBatteryLevelNoNotification() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_NO_NOTIFICATION_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        assertThat(
+                FastPairDecoder.getBatteryLevelNoNotification(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBatteryLevelNoNotification_notIncludedInPacket() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBatteryLevelNoNotification(
+                        fastPairServiceData.createServiceData())).isNull();
+    }
+
+    @Test
+    public void getBatteryLevelNoNotification_noModelId() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00000000, null);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_NO_NOTIFICATION_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        assertThat(
+                FastPairDecoder.getBatteryLevelNoNotification(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBatteryLevelNoNotification_multipleExtraField() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BATTERY_NO_NOTIFICATION_HEADER);
+        fastPairServiceData.mExtraFields.add(BATTERY);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBatteryLevelNoNotification(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BATTERY));
+    }
+
+    @Test
+    public void getBloomFilter() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
     }
 
     @Test
@@ -125,14 +299,222 @@
                 new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
         fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_NO_NOTIFICATION_HEADER);
         fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
-        assertThat(FastPairDecoder.getBloomFilterNoNotification(fastPairServiceData
-                        .createServiceData())).isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+        assertThat(
+                FastPairDecoder.getBloomFilterNoNotification(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilter_smallModelId() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(null, MODEL_ID);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isNull();
+    }
+
+    @Test
+    public void getBloomFilter_headerVersionBitsNotZero() {
+        // Header bits are defined as 0bVVVLLLLR (V=version, L=length, R=reserved), must be zero.
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00100000, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isNull();
+    }
+
+    @Test
+    public void getBloomFilter_noExtraFieldBytesIncluded() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(null);
+        fastPairServiceData.mExtraFields.add(null);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isNull();
+    }
+
+    @Test
+    public void getBloomFilter_extraFieldLengthIsZero() {
+        // The extra field header is formatted as 0bLLLLTTTT (L=length, T=type).
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b00000000);
+        fastPairServiceData.mExtraFields.add(null);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .hasLength(0);
+    }
+
+    @Test
+    public void getBloomFilter_extraFieldLengthLongerThanPacket() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b11110000);
+        fastPairServiceData.mExtraFields.add("1122");
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isNull();
+    }
+
+    @Test
+    public void getBloomFilter_secondExtraFieldLengthLongerThanPacket() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b00100000);
+        fastPairServiceData.mExtraFields.add("1122");
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b11110001);
+        fastPairServiceData.mExtraFields.add("3344");
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData())).isNull();
+    }
+
+    @Test
+    public void getBloomFilter_typeIsWrong() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b01100001);
+        fastPairServiceData.mExtraFields.add("112233445566");
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isNull();
+    }
+
+    @Test
+    public void getBloomFilter_noModelId() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00000000, null);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilter_noModelIdAndMultipleExtraFields() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData((byte) 0b00000000, null);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b00010001);
+        fastPairServiceData.mExtraFields.add("00");
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilter_modelIdAndMultipleExtraFields() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_SALT_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER_SALT);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilterSalt_modelIdAndMultipleExtraFields() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_SALT_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER_SALT);
+        assertThat(
+                FastPairDecoder.getBloomFilterSalt(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER_SALT));
+    }
+
+    @Test
+    public void getBloomFilter_modelIdAndMultipleExtraFieldsWithBloomFilterLast() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b00010001);
+        fastPairServiceData.mExtraFields.add("1A");
+        fastPairServiceData.mExtraFieldHeaders.add((byte) 0b00100010);
+        fastPairServiceData.mExtraFields.add("2CFE");
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilter_modelIdAndMultipleExtraFieldsWithSameType() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(BLOOM_FILTER);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add("000000000000");
+        assertThat(
+                FastPairDecoder.getBloomFilter(fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(BLOOM_FILTER));
+    }
+
+    @Test
+    public void getBloomFilter_longExtraField() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(LONG_BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add(LONG_BLOOM_FILTER);
+        fastPairServiceData.mExtraFieldHeaders.add(BLOOM_FILTER_HEADER);
+        fastPairServiceData.mExtraFields.add("000000000000");
+        assertThat(
+                FastPairDecoder.getBloomFilter(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(LONG_BLOOM_FILTER));
+    }
+
+    @Test
+    public void getRandomResolvableData_whenNoConnectionState() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        assertThat(
+                FastPairDecoder.getRandomResolvableData(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(null);
+    }
+
+    @Test
+    public void getRandomResolvableData_whenContainConnectionState() {
+        FastPairServiceData fastPairServiceData =
+                new FastPairServiceData(MODEL_ID_HEADER, MODEL_ID);
+        fastPairServiceData.mExtraFieldHeaders.add(RANDOM_RESOLVABLE_DATA_HEADER);
+        fastPairServiceData.mExtraFields.add(RANDOM_RESOLVABLE_DATA);
+        assertThat(
+                FastPairDecoder.getRandomResolvableData(
+                        fastPairServiceData.createServiceData()))
+                .isEqualTo(Hex.stringToBytes(RANDOM_RESOLVABLE_DATA));
     }
 
     private static BleRecord newBleRecord(byte[] serviceDataBytes) {
         return parseFromBytes(newFastPairRecord(serviceDataBytes));
     }
-    class FastPairServiceData {
+
+    private static boolean hasModelId(String modelId, FastPairDecoder decoder) {
+        byte[] modelIdBytes = Hex.stringToBytes(modelId);
+        BleRecord bleRecord =
+                parseFromBytes(FastPairTestData.newFastPairRecord((byte) 0, modelIdBytes));
+        return FastPairDecoder.hasBeaconIdBytes(bleRecord)
+                && Arrays.equals(decoder.getBeaconIdBytes(bleRecord), modelIdBytes);
+    }
+
+    private BleSighting bleSighting(byte[] frame) {
+        return new BleSighting(mBluetoothDevice, frame, RSSI,
+                TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()));
+    }
+
+    static class FastPairServiceData {
         private Byte mHeader;
         private String mModelId;
         List<Byte> mExtraFieldHeaders = new ArrayList<>();
@@ -164,6 +546,4 @@
             return serviceData;
         }
     }
-
-
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/RangingUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/RangingUtilsTest.java
index ebe72b3..29f8d4e 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/RangingUtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/RangingUtilsTest.java
@@ -48,8 +48,10 @@
         // RSSI expected at 1 meter based on the calibrated tx field of -41dBm
         // Using params: distance (m), int calibratedTxPower (dBm),
         int rssi = RangingUtils.rssiFromTargetDistance(1.0, -41);
+        int rssi1 = RangingUtils.rssiFromTargetDistance(0.0, -41);
 
         assertThat(rssi).isEqualTo(-82);
+        assertThat(rssi1).isEqualTo(-41);
     }
 
     @Test
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/StringUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/StringUtilsTest.java
new file mode 100644
index 0000000..6b20fdd
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/ble/util/StringUtilsTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.ble.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.ParcelUuid;
+import android.util.SparseArray;
+
+import com.android.server.nearby.common.ble.BleRecord;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class StringUtilsTest {
+    // iBeacon (Apple) Packet 1
+    private static final byte[] BEACON = {
+            // Flags
+            (byte) 0x02,
+            (byte) 0x01,
+            (byte) 0x06,
+            // Manufacturer-specific data header
+            (byte) 0x1a,
+            (byte) 0xff,
+            (byte) 0x4c,
+            (byte) 0x00,
+            // iBeacon Type
+            (byte) 0x02,
+            // Frame length
+            (byte) 0x15,
+            // iBeacon Proximity UUID
+            (byte) 0xf7,
+            (byte) 0x82,
+            (byte) 0x6d,
+            (byte) 0xa6,
+            (byte) 0x4f,
+            (byte) 0xa2,
+            (byte) 0x4e,
+            (byte) 0x98,
+            (byte) 0x80,
+            (byte) 0x24,
+            (byte) 0xbc,
+            (byte) 0x5b,
+            (byte) 0x71,
+            (byte) 0xe0,
+            (byte) 0x89,
+            (byte) 0x3e,
+            // iBeacon Instance ID (Major/Minor)
+            (byte) 0x44,
+            (byte) 0xd0,
+            (byte) 0x25,
+            (byte) 0x22,
+            // Tx Power
+            (byte) 0xb3,
+            // RSP
+            (byte) 0x08,
+            (byte) 0x09,
+            (byte) 0x4b,
+            (byte) 0x6f,
+            (byte) 0x6e,
+            (byte) 0x74,
+            (byte) 0x61,
+            (byte) 0x6b,
+            (byte) 0x74,
+            (byte) 0x02,
+            (byte) 0x0a,
+            (byte) 0xf4,
+            (byte) 0x0a,
+            (byte) 0x16,
+            (byte) 0x0d,
+            (byte) 0xd0,
+            (byte) 0x74,
+            (byte) 0x6d,
+            (byte) 0x4d,
+            (byte) 0x6b,
+            (byte) 0x32,
+            (byte) 0x36,
+            (byte) 0x64,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00
+    };
+
+    @Test
+    public void testToString() {
+        BleRecord record = BleRecord.parseFromBytes(BEACON);
+        assertThat(StringUtils.toString((SparseArray<byte[]>) null)).isEqualTo("null");
+        assertThat(StringUtils.toString(new SparseArray<byte[]>())).isEqualTo("{}");
+        assertThat(StringUtils
+                .toString((Map<ParcelUuid, byte[]>) null)).isEqualTo("null");
+        assertThat(StringUtils
+                .toString(new HashMap<ParcelUuid, byte[]>())).isEqualTo("{}");
+        assertThat(StringUtils.toString(record.getManufacturerSpecificData()))
+                .isEqualTo("{76=[2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128,"
+                        + " 36, -68, 91, 113, -32, -119, 62, 68, -48, 37, 34, -77]}");
+        assertThat(StringUtils.toString(record.getServiceData()))
+                .isEqualTo("{0000d00d-0000-1000-8000-00805f9b34fb="
+                        + "[116, 109, 77, 107, 50, 54, 100]}");
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/BloomFilterTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/BloomFilterTest.java
new file mode 100644
index 0000000..30df81f
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/BloomFilterTest.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bloomfilter;
+
+import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.primitives.Bytes.concat;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Unit-tests for the {@link BloomFilter} class.
+ */
+public class BloomFilterTest {
+    private static final int BYTE_ARRAY_LENGTH = 100;
+
+    private final BloomFilter mBloomFilter =
+            new BloomFilter(new byte[BYTE_ARRAY_LENGTH], newHasher());
+
+    public BloomFilter.Hasher newHasher() {
+        return new FastPairBloomFilterHasher();
+    }
+
+    @Test
+    public void emptyFilter_returnsEmptyArray() throws Exception {
+        assertThat(mBloomFilter.asBytes()).isEqualTo(new byte[BYTE_ARRAY_LENGTH]);
+    }
+
+    @Test
+    public void emptyFilter_neverContains() throws Exception {
+        assertThat(mBloomFilter.possiblyContains(element(1))).isFalse();
+        assertThat(mBloomFilter.possiblyContains(element(2))).isFalse();
+        assertThat(mBloomFilter.possiblyContains(element(3))).isFalse();
+    }
+
+    @Test
+    public void add() throws Exception {
+        assertThat(mBloomFilter.possiblyContains(element(1))).isFalse();
+        mBloomFilter.add(element(1));
+        assertThat(mBloomFilter.possiblyContains(element(1))).isTrue();
+    }
+
+    @Test
+    public void add_onlyGivenArgAdded() throws Exception {
+        mBloomFilter.add(element(1));
+        assertThat(mBloomFilter.possiblyContains(element(1))).isTrue();
+        assertThat(mBloomFilter.possiblyContains(element(2))).isFalse();
+        assertThat(mBloomFilter.possiblyContains(element(3))).isFalse();
+    }
+
+    @Test
+    public void add_multipleArgs() throws Exception {
+        mBloomFilter.add(element(1));
+        mBloomFilter.add(element(2));
+        assertThat(mBloomFilter.possiblyContains(element(1))).isTrue();
+        assertThat(mBloomFilter.possiblyContains(element(2))).isTrue();
+        assertThat(mBloomFilter.possiblyContains(element(3))).isFalse();
+    }
+
+    /**
+     * This test was added because of a bug where the BloomFilter doesn't utilize all bits given.
+     * Functionally, the filter still works, but we just have a much higher false positive rate. The
+     * bug was caused by confusing bit length and byte length, which made our BloomFilter only set
+     * bits on the first byteLength (bitLength / 8) bits rather than the whole bitLength bits.
+     *
+     * <p>Here, we're verifying that the bits set are somewhat scattered. So instead of something
+     * like [ 0, 1, 1, 0, 0, 0, 0, ..., 0 ], we should be getting something like
+     * [ 0, 1, 0, 0, 1, 1, 0, 0,0, 1, ..., 1, 0].
+     */
+    @Test
+    public void randomness_noEndBias() throws Exception {
+        // Add one element to our BloomFilter.
+        mBloomFilter.add(element(1));
+
+        // Record the amount of non-zero bytes and the longest streak of zero bytes in the resulting
+        // BloomFilter. This is an approximation of reasonable distribution since we're recording by
+        // bytes instead of bits.
+        int nonZeroCount = 0;
+        int longestZeroStreak = 0;
+        int currentZeroStreak = 0;
+        for (byte b : mBloomFilter.asBytes()) {
+            if (b == 0) {
+                currentZeroStreak++;
+            } else {
+                // Increment the number of non-zero bytes we've seen, update the longest zero
+                // streak, and then reset the current zero streak.
+                nonZeroCount++;
+                longestZeroStreak = Math.max(longestZeroStreak, currentZeroStreak);
+                currentZeroStreak = 0;
+            }
+        }
+        // Update the longest zero streak again for the tail case.
+        longestZeroStreak = Math.max(longestZeroStreak, currentZeroStreak);
+
+        // Since randomness is hard to measure within one unit test, we instead do a valid check.
+        // All non-zero bytes should not be packed into one end of the array.
+        //
+        // In this case, the size of one end is approximated to be:
+        //   BYTE_ARRAY_LENGTH / nonZeroCount.
+        // Therefore, the longest zero streak should be less than:
+        //   BYTE_ARRAY_LENGTH - one end of the array.
+        int longestAcceptableZeroStreak = BYTE_ARRAY_LENGTH - (BYTE_ARRAY_LENGTH / nonZeroCount);
+        assertThat(longestZeroStreak).isAtMost(longestAcceptableZeroStreak);
+    }
+
+    @Test
+    public void randomness_falsePositiveRate() throws Exception {
+        // Create a new BloomFilter with a length of only 10 bytes.
+        BloomFilter bloomFilter = new BloomFilter(new byte[10], newHasher());
+
+        // Add 5 distinct elements to the BloomFilter.
+        for (int i = 0; i < 5; i++) {
+            bloomFilter.add(element(i));
+        }
+
+        // Now test 100 other elements and record the number of false positives.
+        int falsePositives = 0;
+        for (int i = 5; i < 105; i++) {
+            falsePositives += bloomFilter.possiblyContains(element(i)) ? 1 : 0;
+        }
+
+        // We expect the false positive rate to be 3% with 5 elements in a 10 byte filter. Thus,
+        // we give a little leeway and verify that the false positive rate is no more than 5%.
+        assertWithMessage(
+                String.format(
+                        "False positive rate too large. Expected <= 5%%, but got %d%%.",
+                        falsePositives))
+                .that(falsePositives <= 5)
+                .isTrue();
+        System.out.printf("False positive rate: %d%%%n", falsePositives);
+    }
+
+
+    private String element(int index) {
+        return "ELEMENT_" + index;
+    }
+
+    @Test
+    public void specificBitPattern() throws Exception {
+        // Create a new BloomFilter along with a fixed set of elements
+        // and bit patterns to verify with.
+        BloomFilter bloomFilter = new BloomFilter(new byte[6], newHasher());
+        // Combine an account key and mac address.
+        byte[] element =
+                concat(
+                        base16().decode("11223344556677889900AABBCCDDEEFF"),
+                        base16().withSeparator(":", 2).decode("84:68:3E:00:02:11"));
+        byte[] expectedBitPattern = new byte[] {0x50, 0x00, 0x04, 0x15, 0x08, 0x01};
+
+        // Add the fixed elements to the filter.
+        bloomFilter.add(element);
+
+        // Verify that the resulting bytes match the expected one.
+        byte[] bloomFilterBytes = bloomFilter.asBytes();
+        assertWithMessage(
+                "Unexpected bit pattern. Expected %s, but got %s.",
+                base16().encode(expectedBitPattern), base16().encode(bloomFilterBytes))
+                .that(Arrays.equals(expectedBitPattern, bloomFilterBytes))
+                .isTrue();
+
+        // Verify that the expected bit pattern creates a BloomFilter containing all fixed elements.
+        bloomFilter = new BloomFilter(expectedBitPattern, newHasher());
+        assertThat(bloomFilter.possiblyContains(element)).isTrue();
+    }
+
+    // This test case has been on the spec,
+    // https://devsite.googleplex.com/nearby/fast-pair/spec#test_cases.
+    // Explicitly adds it here, and we can easily change the parameters (e.g. account key, ble
+    // address) to clarify test results with partners.
+    @Test
+    public void specificBitPattern_hasOneAccountKey() {
+        BloomFilter bloomFilter1 = new BloomFilter(new byte[4], newHasher());
+        BloomFilter bloomFilter2 = new BloomFilter(new byte[4], newHasher());
+        byte[] accountKey = base16().decode("11223344556677889900AABBCCDDEEFF");
+        byte[] salt1 = base16().decode("C7");
+        byte[] salt2 = base16().decode("C7C8");
+
+        // Add the fixed elements to the filter.
+        bloomFilter1.add(concat(accountKey, salt1));
+        bloomFilter2.add(concat(accountKey, salt2));
+
+        assertThat(bloomFilter1.asBytes()).isEqualTo(base16().decode("0A428810"));
+        assertThat(bloomFilter2.asBytes()).isEqualTo(base16().decode("020C802A"));
+    }
+
+    // Adds this test case to spec. We can easily change the parameters (e.g. account key, ble
+    // address, battery data) to clarify test results with partners.
+    @Test
+    public void specificBitPattern_hasOneAccountKey_withBatteryData() {
+        BloomFilter bloomFilter1 = new BloomFilter(new byte[4], newHasher());
+        BloomFilter bloomFilter2 = new BloomFilter(new byte[4], newHasher());
+        byte[] accountKey = base16().decode("11223344556677889900AABBCCDDEEFF");
+        byte[] salt1 = base16().decode("C7");
+        byte[] salt2 = base16().decode("C7C8");
+        byte[] batteryData = {
+                0b00110011, // length = 3, show UI indication.
+                0b01000000, // Left bud: not charging, battery level = 64.
+                0b01000000, // Right bud: not charging, battery level = 64.
+                0b01000000 // Case: not charging, battery level = 64.
+        };
+
+        // Adds battery data to build bloom filter.
+        bloomFilter1.add(concat(accountKey, salt1, batteryData));
+        bloomFilter2.add(concat(accountKey, salt2, batteryData));
+
+        assertThat(bloomFilter1.asBytes()).isEqualTo(base16().decode("4A00F000"));
+        assertThat(bloomFilter2.asBytes()).isEqualTo(base16().decode("0101460A"));
+    }
+
+    // This test case has been on the spec,
+    // https://devsite.googleplex.com/nearby/fast-pair/spec#test_cases.
+    // Explicitly adds it here, and we can easily change the parameters (e.g. account key, ble
+    // address) to clarify test results with partners.
+    @Test
+    public void specificBitPattern_hasTwoAccountKeys() {
+        BloomFilter bloomFilter1 = new BloomFilter(new byte[5], newHasher());
+        BloomFilter bloomFilter2 = new BloomFilter(new byte[5], newHasher());
+        byte[] accountKey1 = base16().decode("11223344556677889900AABBCCDDEEFF");
+        byte[] accountKey2 = base16().decode("11112222333344445555666677778888");
+        byte[] salt1 = base16().decode("C7");
+        byte[] salt2 = base16().decode("C7C8");
+
+        // Adds the fixed elements to the filter.
+        bloomFilter1.add(concat(accountKey1, salt1));
+        bloomFilter1.add(concat(accountKey2, salt1));
+        bloomFilter2.add(concat(accountKey1, salt2));
+        bloomFilter2.add(concat(accountKey2, salt2));
+
+        assertThat(bloomFilter1.asBytes()).isEqualTo(base16().decode("2FBA064200"));
+        assertThat(bloomFilter2.asBytes()).isEqualTo(base16().decode("844A62208B"));
+    }
+
+    // Adds this test case to spec. We can easily change the parameters (e.g. account keys, ble
+    // address, battery data) to clarify test results with partners.
+    @Test
+    public void specificBitPattern_hasTwoAccountKeys_withBatteryData() {
+        BloomFilter bloomFilter1 = new BloomFilter(new byte[5], newHasher());
+        BloomFilter bloomFilter2 = new BloomFilter(new byte[5], newHasher());
+        byte[] accountKey1 = base16().decode("11223344556677889900AABBCCDDEEFF");
+        byte[] accountKey2 = base16().decode("11112222333344445555666677778888");
+        byte[] salt1 = base16().decode("C7");
+        byte[] salt2 = base16().decode("C7C8");
+        byte[] batteryData = {
+                0b00110011, // length = 3, show UI indication.
+                0b01000000, // Left bud: not charging, battery level = 64.
+                0b01000000, // Right bud: not charging, battery level = 64.
+                0b01000000 // Case: not charging, battery level = 64.
+        };
+
+        // Adds battery data to build bloom filter.
+        bloomFilter1.add(concat(accountKey1, salt1, batteryData));
+        bloomFilter1.add(concat(accountKey2, salt1, batteryData));
+        bloomFilter2.add(concat(accountKey1, salt2, batteryData));
+        bloomFilter2.add(concat(accountKey2, salt2, batteryData));
+
+        assertThat(bloomFilter1.asBytes()).isEqualTo(base16().decode("102256C04D"));
+        assertThat(bloomFilter2.asBytes()).isEqualTo(base16().decode("461524D008"));
+    }
+
+    // Adds this test case to spec. We can easily change the parameters (e.g. account keys, ble
+    // address, battery data and battery remaining time) to clarify test results with partners.
+    @Test
+    public void specificBitPattern_hasTwoAccountKeys_withBatteryLevelAndRemainingTime() {
+        BloomFilter bloomFilter1 = new BloomFilter(new byte[5], newHasher());
+        BloomFilter bloomFilter2 = new BloomFilter(new byte[5], newHasher());
+        byte[] accountKey1 = base16().decode("11223344556677889900AABBCCDDEEFF");
+        byte[] accountKey2 = base16().decode("11112222333344445555666677778888");
+        byte[] salt1 = base16().decode("C7");
+        byte[] salt2 = base16().decode("C7C8");
+        byte[] batteryData = {
+                0b00110011, // length = 3, show UI indication.
+                0b01000000, // Left bud: not charging, battery level = 64.
+                0b01000000, // Right bud: not charging, battery level = 64.
+                0b01000000 // Case: not charging, battery level = 64.
+        };
+        byte[] batteryRemainingTime = {
+                0b00010101, // length = 1, type = 0b0101 (remaining battery time).
+                0x1E, // remaining battery time (in minutes) =  30 minutes.
+        };
+
+        // Adds battery data to build bloom filter.
+        bloomFilter1.add(concat(accountKey1, salt1, batteryData, batteryRemainingTime));
+        bloomFilter1.add(concat(accountKey2, salt1, batteryData, batteryRemainingTime));
+        bloomFilter2.add(concat(accountKey1, salt2, batteryData, batteryRemainingTime));
+        bloomFilter2.add(concat(accountKey2, salt2, batteryData, batteryRemainingTime));
+
+        assertThat(bloomFilter1.asBytes()).isEqualTo(base16().decode("32A086B41A"));
+        assertThat(bloomFilter2.asBytes()).isEqualTo(base16().decode("C2A042043E"));
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasherTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasherTest.java
new file mode 100644
index 0000000..92c3b1a
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bloomfilter/FastPairBloomFilterHasherTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bloomfilter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+
+public class FastPairBloomFilterHasherTest {
+    private static final Charset CHARSET = UTF_8;
+    private static FastPairBloomFilterHasher sFastPairBloomFilterHasher =
+            new FastPairBloomFilterHasher();
+    @Test
+    public void getHashes() {
+        int[] hashe1 = sFastPairBloomFilterHasher.getHashes(element(1).getBytes(CHARSET));
+        int[] hashe2 = sFastPairBloomFilterHasher.getHashes(element(1).getBytes(CHARSET));
+        int[] hashe3 = sFastPairBloomFilterHasher.getHashes(element(2).getBytes(CHARSET));
+        assertThat(hashe1).isEqualTo(hashe2);
+        assertThat(hashe1).isNotEqualTo(hashe3);
+    }
+
+    private String element(int index) {
+        return "ELEMENT_" + index;
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoderTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoderTest.java
index 28d2fca..cf40fc6 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/AdditionalDataEncoderTest.java
@@ -63,6 +63,21 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void inputNullSecretKeyToEncode_mustThrowException() {
+        byte[] rawData = base16().decode("00112233445566778899AABBCCDDEEFF");
+
+        GeneralSecurityException exception =
+                assertThrows(
+                        GeneralSecurityException.class,
+                        () -> AdditionalDataEncoder.encodeAdditionalDataPacket(null, rawData));
+
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("Incorrect secret for encoding additional data packet");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void inputIncorrectKeySizeToEncode_mustThrowException() {
         byte[] secret = new byte[KEY_LENGTH - 1];
         byte[] rawData = base16().decode("00112233445566778899AABBCCDDEEFF");
@@ -79,6 +94,54 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void inputNullAdditionalDataToEncode_mustThrowException()
+            throws GeneralSecurityException {
+        byte[] secret = AesCtrMultipleBlockEncryption.generateKey();
+
+        GeneralSecurityException exception =
+                assertThrows(
+                        GeneralSecurityException.class,
+                        () -> AdditionalDataEncoder.encodeAdditionalDataPacket(secret, null));
+
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("Invalid data for encoding additional data packet");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void inputInvalidAdditionalDataSizeToEncode_mustThrowException()
+            throws GeneralSecurityException {
+        byte[] secret = AesCtrMultipleBlockEncryption.generateKey();
+        byte[] rawData = base16().decode("");
+        GeneralSecurityException exception =
+                assertThrows(
+                        GeneralSecurityException.class,
+                        () -> AdditionalDataEncoder.encodeAdditionalDataPacket(secret, rawData));
+
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("Invalid data for encoding additional data packet");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void inputTooLargeAdditionalDataSizeToEncode_mustThrowException()
+            throws GeneralSecurityException {
+        byte[] secret = AesCtrMultipleBlockEncryption.generateKey();
+        byte[] rawData = new byte[MAX_LENGTH_OF_DATA + 1];
+        GeneralSecurityException exception =
+                assertThrows(
+                        GeneralSecurityException.class,
+                        () -> AdditionalDataEncoder.encodeAdditionalDataPacket(secret, rawData));
+
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("Invalid data for encoding additional data packet");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void inputIncorrectKeySizeToDecode_mustThrowException() {
         byte[] secret = new byte[KEY_LENGTH - 1];
         byte[] packet = base16().decode("01234567890123456789");
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairerTest.java
index 0a56f2f..6871024 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothAudioPairerTest.java
@@ -84,6 +84,23 @@
     }
 
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void getDevice() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        try {
+            assertThat(new BluetoothAudioPairer(
+                    context,
+                    BLUETOOTH_DEVICE,
+                    Preferences.builder().build(),
+                    new EventLoggerWrapper(new TestEventLogger()),
+                    null /* KeyBasePairingInfo */,
+                    null /*PasskeyConfirmationHandler */,
+                    new TimingLogger(EVENT_NAME, Preferences.builder().build())).getDevice())
+                    .isEqualTo(BLUETOOTH_DEVICE);
+        } catch (PairingException e) {
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void testBluetoothAudioPairerUnpairNoCrash() {
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         try {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairerTest.java
new file mode 100644
index 0000000..48f4b9b
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BluetoothClassicPairerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class BluetoothClassicPairerTest {
+    @Mock
+    PasskeyConfirmationHandler mPasskeyConfirmationHandler;
+    private static final BluetoothDevice BLUETOOTH_DEVICE = BluetoothAdapter.getDefaultAdapter()
+            .getRemoteDevice("11:22:33:44:55:66");
+    private  BluetoothClassicPairer mBluetoothClassicPairer;
+
+    @Before
+    public void setUp() {
+        mBluetoothClassicPairer = new BluetoothClassicPairer(
+                ApplicationProvider.getApplicationContext(),
+                BLUETOOTH_DEVICE,
+                Preferences.builder().build(),
+                mPasskeyConfirmationHandler);
+    }
+
+    @Test
+    public void pair() throws PairingException {
+        PairingException exception =
+                assertThrows(
+                        PairingException.class,
+                        () -> mBluetoothClassicPairer.pair());
+
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("BluetoothClassicPairer, createBond");
+        assertThat(mBluetoothClassicPairer.isPaired()).isFalse();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java
new file mode 100644
index 0000000..9450d60
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import junit.framework.TestCase;
+
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link Bytes}.
+ */
+public class BytesTest extends TestCase {
+
+    private static final Bytes.Value VALUE1 =
+            new Bytes.Value(new byte[]{1, 2}, ByteOrder.BIG_ENDIAN);
+    private static final Bytes.Value VALUE2 =
+            new Bytes.Value(new byte[]{1, 2}, ByteOrder.BIG_ENDIAN);
+    private static final Bytes.Value VALUE3 =
+            new Bytes.Value(new byte[]{1, 3}, ByteOrder.BIG_ENDIAN);
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEquals_asExpected()  {
+        assertThat(VALUE1.equals(VALUE2)).isTrue();
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testNotEquals_asExpected()  {
+        assertThat(VALUE1.equals(VALUE3)).isFalse();
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBytes_asExpected()  {
+        assertThat(Arrays.equals(VALUE1.getBytes(ByteOrder.BIG_ENDIAN), new byte[]{1, 2})).isTrue();
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testToString()  {
+        assertThat(VALUE1.toString()).isEqualTo("0102");
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testReverse()  {
+        assertThat(VALUE1.reverse(new byte[]{1, 2})).isEqualTo(new byte[]{2, 1});
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
index f7ffa24..6684fbc 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
@@ -33,6 +33,8 @@
 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic;
 import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
 
+import com.google.common.collect.ImmutableList;
+
 import junit.framework.TestCase;
 
 import org.mockito.Mock;
@@ -44,6 +46,8 @@
  */
 public class ConstantsTest extends TestCase {
 
+    private static final int PASSKEY = 32689;
+
     @Mock
     private BluetoothGattConnection mMockGattConnection;
 
@@ -78,4 +82,62 @@
         assertThat(KeyBasedPairingCharacteristic.getId(mMockGattConnection))
                 .isEqualTo(OLD_KEY_BASE_PAIRING_CHARACTERISTICS);
     }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_accountKeyCharacteristic_notCrash() throws BluetoothException {
+        Constants.FastPairService.AccountKeyCharacteristic.getId(mMockGattConnection);
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_additionalDataCharacteristic_notCrash() throws BluetoothException {
+        Constants.FastPairService.AdditionalDataCharacteristic.getId(mMockGattConnection);
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_nameCharacteristic_notCrash() throws BluetoothException {
+        Constants.FastPairService.NameCharacteristic.getId(mMockGattConnection);
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_passKeyCharacteristic_encryptDecryptSuccessfully()
+            throws java.security.GeneralSecurityException {
+        byte[] secret = AesEcbSingleBlockEncryption.generateKey();
+
+        Constants.FastPairService.PasskeyCharacteristic.getId(mMockGattConnection);
+        assertThat(
+                Constants.FastPairService.PasskeyCharacteristic.decrypt(
+                        Constants.FastPairService.PasskeyCharacteristic.Type.SEEKER,
+                        secret,
+                        Constants.FastPairService.PasskeyCharacteristic.encrypt(
+                                Constants.FastPairService.PasskeyCharacteristic.Type.SEEKER,
+                                secret,
+                                PASSKEY))
+        ).isEqualTo(PASSKEY);
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_beaconActionsCharacteristic_notCrash() throws BluetoothException {
+        Constants.FastPairService.BeaconActionsCharacteristic.getId(mMockGattConnection);
+        for (byte b : ImmutableList.of(
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .READ_BEACON_PARAMETERS,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .READ_PROVISIONING_STATE,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .SET_EPHEMERAL_IDENTITY_KEY,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .CLEAR_EPHEMERAL_IDENTITY_KEY,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .READ_EPHEMERAL_IDENTITY_KEY,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .RING,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .READ_RINGING_STATE,
+                (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+                        .UNKNOWN
+        )) {
+            assertThat(Constants.FastPairService.BeaconActionsCharacteristic
+                    .valueOf(b)).isEqualTo(b);
+        }
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java
new file mode 100644
index 0000000..052e696
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.intdefs.FastPairEventIntDefs;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link CreateBondException}.
+ */
+public class CreateBondExceptionTest extends TestCase {
+
+    private static final String FORMAT = "FORMAT";
+    private static final int REASON = 0;
+    private static final CreateBondException EXCEPTION = new CreateBondException(
+            FastPairEventIntDefs.CreateBondErrorCode.INCORRECT_VARIANT, REASON, FORMAT);
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_getter_asExpected() throws BluetoothException {
+        assertThat(EXCEPTION.getErrorCode()).isEqualTo(
+                FastPairEventIntDefs.CreateBondErrorCode.INCORRECT_VARIANT);
+        assertThat(EXCEPTION.getReason()).isSameInstanceAs(REASON);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java
new file mode 100644
index 0000000..94bf111
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link DeviceIntentReceiver}.
+ */
+public class DeviceIntentReceiverTest extends TestCase {
+    @Mock Preferences mPreferences;
+    @Mock BluetoothDevice mBluetoothDevice;
+
+    private DeviceIntentReceiver mDeviceIntentReceiver;
+    private Intent mIntent;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        initMocks(this);
+
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mDeviceIntentReceiver = DeviceIntentReceiver.oneShotReceiver(
+                context, mPreferences, mBluetoothDevice);
+
+        mIntent = new Intent().putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+    }
+
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_onReceive_notCrash() throws Exception {
+        mDeviceIntentReceiver.onReceive(mIntent);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
index 28e925f..1b63ad0 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
@@ -58,6 +58,13 @@
                         .setBluetoothDevice(BLUETOOTH_DEVICE)
                         .setProfile(PROFILE)
                         .build();
+        assertThat(event.hasBluetoothDevice()).isTrue();
+        assertThat(event.hasProfile()).isTrue();
+        assertThat(event.isFailure()).isTrue();
+        assertThat(event.toString()).isEqualTo(
+                "Event{eventCode=1120, timestamp=1234, profile=1, "
+                        + "bluetoothDevice=11:22:33:44:55:66, "
+                        + "exception=java.lang.Exception: Test exception}");
 
         Parcel parcel = Parcel.obtain();
         event.writeToParcel(parcel, event.describeContents());
@@ -70,5 +77,8 @@
         assertThat(result.getEventCode()).isEqualTo(event.getEventCode());
         assertThat(result.getBluetoothDevice()).isEqualTo(event.getBluetoothDevice());
         assertThat(result.getProfile()).isEqualTo(event.getProfile());
+        assertThat(result.equals(event)).isTrue();
+
+        assertThat(Event.CREATOR.newArray(10)).isNotEmpty();
     }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnectionTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnectionTest.java
index a103a72..63c39b2 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnectionTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/FastPairDualConnectionTest.java
@@ -23,9 +23,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyShort;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -45,6 +47,7 @@
 
 import junit.framework.TestCase;
 
+import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -75,6 +78,8 @@
     private TestEventLogger mEventLogger;
     @Mock private TimingLogger mTimingLogger;
     @Mock private BluetoothAudioPairer mBluetoothAudioPairer;
+    @Mock private android.bluetooth.BluetoothAdapter mBluetoothAdapter;
+    @Mock FastPairDualConnection mFastPairDualConnection;
 
     @Override
     public void setUp() throws Exception {
@@ -287,6 +292,23 @@
         }
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testReflectionException()
+            throws BluetoothException, ReflectionException, ExecutionException,
+            InterruptedException, PairingException, TimeoutException {
+        when(mFastPairDualConnection.pair())
+                .thenThrow(new ReflectionException(
+                        new NoSuchMethodException("testReflectionException")));
+        ReflectionException exception =
+                assertThrows(
+                        ReflectionException.class,
+                        () -> mFastPairDualConnection.pair());
+        assertThat(exception)
+                .hasMessageThat()
+                .contains("testReflectionException");
+    }
+
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void testHistoryItem() {
         FastPairDualConnection connection = new FastPairDualConnection(
@@ -307,6 +329,11 @@
                 .isFalse();
     }
 
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetLeState() throws ReflectionException {
+        FastPairDualConnection.getLeState(mBluetoothAdapter);
+    }
+
     static class TestEventLogger implements EventLogger {
 
         private List<Item> mLogs = new ArrayList<>();
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java
new file mode 100644
index 0000000..e53e60f
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.BLUETOOTH_ADDRESS_LENGTH;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.DEVICE_ACTION;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.ADDITIONAL_DATA_TYPE_INDEX;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.Nullable;
+import androidx.core.util.Consumer;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.common.bluetooth.BluetoothGattException;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request;
+import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
+import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter;
+import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor;
+import com.android.server.nearby.intdefs.NearbyEventIntDefs;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.BaseEncoding;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.security.GeneralSecurityException;
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link HandshakeHandler}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HandshakeHandlerTest {
+
+    public static final byte[] PUBLIC_KEY =
+            BaseEncoding.base64().decode(
+                    "d2JTfvfdS6u7LmGfMOmco3C7ra3lW1k17AOly0LrBydDZURacfTY"
+                            + "IMmo5K1ejfD9e8b6qHsDTNzselhifi10kQ==");
+    private static final String SEEKER_ADDRESS = "A1:A2:A3:A4:A5:A6";
+    private static final String PROVIDER_BLE_ADDRESS = "11:22:33:44:55:66";
+    /**
+     * The random-resolvable private address (RPA) is sometimes used when advertising over BLE, to
+     * hide the static public address (otherwise, the Fast Pair device would b
+     * identifiable/trackable whenever it's BLE advertising).
+     */
+    private static final String RANDOM_PRIVATE_ADDRESS = "BB:BB:BB:BB:BB:1E";
+    private static final byte[] SHARED_SECRET =
+            BaseEncoding.base16().decode("0123456789ABCDEF0123456789ABCDEF");
+
+    @Mock EventLoggerWrapper mEventLoggerWrapper;
+    @Mock BluetoothGattConnection mBluetoothGattConnection;
+    @Mock BluetoothGattConnection.ChangeObserver mChangeObserver;
+    @Mock private Consumer<Integer> mRescueFromError;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void handshakeGattError_noRetryError_failed() throws BluetoothException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .build();
+        BluetoothGattException exception =
+                new BluetoothGattException("Exception for no retry", 257);
+        when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+        GattConnectionManager gattConnectionManager =
+                createGattConnectionManager(Preferences.builder(), () -> {});
+        gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+        when(mBluetoothGattConnection.enableNotification(any(), any()))
+                .thenReturn(mChangeObserver);
+        InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+        assertThrows(
+                BluetoothGattException.class,
+                () ->
+                        getHandshakeHandler(gattConnectionManager, address -> address)
+                                .doHandshakeWithRetryAndSignalLostCheck(
+                                        PUBLIC_KEY,
+                                        keyBasedPairingRequest,
+                                        mRescueFromError));
+
+        inOrder.verify(mEventLoggerWrapper).setCurrentEvent(
+                NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void handshakeGattError_retryAndNoCount_throwException() throws BluetoothException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .build();
+        BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+        when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+        GattConnectionManager gattConnectionManager =
+                createGattConnectionManager(Preferences.builder(), () -> {});
+        gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+        when(mBluetoothGattConnection.enableNotification(any(), any()))
+                .thenReturn(mChangeObserver);
+        InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+        HandshakeHandler.HandshakeException handshakeException =
+                assertThrows(
+                        HandshakeHandler.HandshakeException.class,
+                        () -> getHandshakeHandler(gattConnectionManager, address -> address)
+                                .doHandshakeWithRetryAndSignalLostCheck(
+                                        PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verifyNoMoreInteractions();
+        assertThat(handshakeException.getOriginalException()).isEqualTo(exception);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void handshakeGattError_noRetryOnTimeout_throwException() throws BluetoothException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .build();
+        BluetoothOperationExecutor.BluetoothOperationTimeoutException exception =
+                new BluetoothOperationExecutor.BluetoothOperationTimeoutException("Test timeout");
+        when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+        GattConnectionManager gattConnectionManager =
+                createGattConnectionManager(Preferences.builder(), () -> {});
+        gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+        when(mBluetoothGattConnection.enableNotification(any(), any()))
+                .thenReturn(mChangeObserver);
+        InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+        assertThrows(
+                HandshakeHandler.HandshakeException.class,
+                () ->
+                        new HandshakeHandler(
+                                gattConnectionManager,
+                                PROVIDER_BLE_ADDRESS,
+                                Preferences.builder().setRetrySecretHandshakeTimeout(false).build(),
+                                mEventLoggerWrapper,
+                                address -> address)
+                                .doHandshakeWithRetryAndSignalLostCheck(
+                                        PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void handshakeGattError_signalLost() throws BluetoothException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .build();
+        BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+        when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+        GattConnectionManager gattConnectionManager =
+                createGattConnectionManager(Preferences.builder(), () -> {});
+        gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+        when(mBluetoothGattConnection.enableNotification(any(), any()))
+                .thenReturn(mChangeObserver);
+        InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+        SignalLostException signalLostException =
+                assertThrows(
+                        SignalLostException.class,
+                        () -> getHandshakeHandler(gattConnectionManager, address -> null)
+                                .doHandshakeWithRetryAndSignalLostCheck(
+                                        PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+        inOrder.verify(mEventLoggerWrapper)
+                .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        assertThat(signalLostException).hasCauseThat().isEqualTo(exception);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void handshakeGattError_addressRotate() throws BluetoothException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .build();
+        BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+        when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+        GattConnectionManager gattConnectionManager =
+                createGattConnectionManager(Preferences.builder(), () -> {});
+        gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+        when(mBluetoothGattConnection.enableNotification(any(), any()))
+                .thenReturn(mChangeObserver);
+        InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+        SignalRotatedException signalRotatedException =
+                assertThrows(
+                        SignalRotatedException.class,
+                        () -> getHandshakeHandler(
+                                gattConnectionManager, address -> "AA:BB:CC:DD:EE:FF")
+                                .doHandshakeWithRetryAndSignalLostCheck(
+                                        PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+        inOrder.verify(mEventLoggerWrapper).setCurrentEvent(
+                NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+        inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+        assertThat(signalRotatedException.getNewAddress()).isEqualTo("AA:BB:CC:DD:EE:FF");
+        assertThat(signalRotatedException).hasCauseThat().isEqualTo(exception);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void constructBytes_setRetroactiveFlag_decodeCorrectly() throws
+            GeneralSecurityException {
+        HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+                new HandshakeHandler.KeyBasedPairingRequest.Builder()
+                        .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+                        .addFlag(REQUEST_RETROACTIVE_PAIR)
+                        .setSeekerPublicAddress(BluetoothAddress.decode(SEEKER_ADDRESS))
+                        .build();
+
+        byte[] encryptedRawMessage =
+                AesEcbSingleBlockEncryption.encrypt(
+                        SHARED_SECRET, keyBasedPairingRequest.getBytes());
+        HandshakeRequest handshakeRequest =
+                new HandshakeRequest(SHARED_SECRET, encryptedRawMessage);
+
+        assertThat(handshakeRequest.getType())
+                .isEqualTo(HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST);
+        assertThat(handshakeRequest.requestRetroactivePair()).isTrue();
+        assertThat(handshakeRequest.getVerificationData())
+                .isEqualTo(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS));
+        assertThat(handshakeRequest.getSeekerPublicAddress())
+                .isEqualTo(BluetoothAddress.decode(SEEKER_ADDRESS));
+        assertThat(handshakeRequest.requestDeviceName()).isFalse();
+        assertThat(handshakeRequest.requestDiscoverable()).isFalse();
+        assertThat(handshakeRequest.requestProviderInitialBonding()).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void getTimeout_notOverShortRetryMaxSpentTime_getShort() {
+        Preferences preferences = Preferences.builder().build();
+
+        assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true)
+                .getTimeoutMs(
+                        preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs()
+                                - 1))
+                .isEqualTo(preferences.getSecretHandshakeShortTimeoutMs());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void getTimeout_overShortRetryMaxSpentTime_getLong() {
+        Preferences preferences = Preferences.builder().build();
+
+        assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true)
+                .getTimeoutMs(
+                        preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs()
+                                + 1))
+                .isEqualTo(preferences.getSecretHandshakeLongTimeoutMs());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void getTimeout_retryNotEnabled_getOrigin() {
+        Preferences preferences = Preferences.builder().build();
+
+        assertThat(
+                new HandshakeHandler(
+                        createGattConnectionManager(Preferences.builder(), () -> {}),
+                        PROVIDER_BLE_ADDRESS,
+                        Preferences.builder()
+                                .setRetryGattConnectionAndSecretHandshake(false).build(),
+                        mEventLoggerWrapper,
+                        /* fastPairSignalChecker= */ null)
+                        .getTimeoutMs(0))
+                .isEqualTo(Duration.ofSeconds(
+                        preferences.getGattOperationTimeoutSeconds()).toMillis());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void triggersActionOverBle_notCrash() {
+        HandshakeHandler.ActionOverBle.Builder actionOverBleBuilder =
+                new HandshakeHandler.ActionOverBle.Builder()
+                        .addFlag(
+                                Constants.FastPairService.KeyBasedPairingCharacteristic
+                                        .ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC)
+                        .setVerificationData(BluetoothAddress.decode(RANDOM_PRIVATE_ADDRESS))
+                        .setAdditionalDataType(AdditionalDataType.PERSONALIZED_NAME)
+                        .setEvent(0, 0)
+                        .setEventAdditionalData(new byte[]{1})
+                        .getThis();
+        HandshakeHandler.ActionOverBle actionOverBle = actionOverBleBuilder.build();
+        assertThat(actionOverBle.getBytes().length).isEqualTo(16);
+        assertThat(
+                Arrays.equals(
+                        Arrays.copyOfRange(actionOverBle.getBytes(), 0, 12),
+                        new byte[]{
+                                (byte) 16, (byte)  -64, (byte) -69, (byte) -69,
+                                (byte) -69, (byte)  -69, (byte) -69, (byte) 30,
+                                (byte) 0, (byte) 0, (byte) 1, (byte) 1}))
+                .isTrue();
+    }
+
+    private GattConnectionManager createGattConnectionManager(
+            Preferences.Builder prefs, ToggleBluetoothTask toggleBluetooth) {
+        return new GattConnectionManager(
+                ApplicationProvider.getApplicationContext(),
+                prefs.build(),
+                new EventLoggerWrapper(null),
+                BluetoothAdapter.getDefaultAdapter(),
+                toggleBluetooth,
+                PROVIDER_BLE_ADDRESS,
+                new TimingLogger("GattConnectionManager", prefs.build()),
+                /* fastPairSignalChecker= */ null,
+                /* setMtu= */ false);
+    }
+
+    private HandshakeHandler getHandshakeHandler(
+            GattConnectionManager gattConnectionManager,
+            @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker) {
+        return new HandshakeHandler(
+                gattConnectionManager,
+                PROVIDER_BLE_ADDRESS,
+                Preferences.builder()
+                        .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of(257))
+                        .setRetrySecretHandshakeTimeout(true)
+                        .build(),
+                mEventLoggerWrapper,
+                fastPairSignalChecker);
+    }
+
+    private HandshakeHandler getHandshakeHandler(
+            boolean getEnable128BitCustomGattCharacteristicsId) {
+        return new HandshakeHandler(
+                createGattConnectionManager(Preferences.builder(), () -> {}),
+                PROVIDER_BLE_ADDRESS,
+                Preferences.builder()
+                        .setGattOperationTimeoutSeconds(5)
+                        .setEnable128BitCustomGattCharacteristicsId(
+                                getEnable128BitCustomGattCharacteristicsId)
+                        .build(),
+                mEventLoggerWrapper,
+                /* fastPairSignalChecker= */ null);
+    }
+
+    private static class HandshakeRequest {
+
+        /**
+         * 16 bytes data: 1-byte for type, 1-byte for flags, 6-bytes for provider's BLE address, 8
+         * bytes optional data.
+         *
+         * @see {go/fast-pair-spec-handshake-message1}
+         */
+        private final byte[] mDecryptedMessage;
+
+        HandshakeRequest(byte[] key, byte[] encryptedPairingRequest)
+                throws GeneralSecurityException {
+            mDecryptedMessage = AesEcbSingleBlockEncryption.decrypt(key, encryptedPairingRequest);
+        }
+
+        /**
+         * Gets the type of this handshake request. Currently, we have 2 types: 0x00 for Key-based
+         * Pairing Request and 0x10 for Action Request.
+         */
+        public Type getType() {
+            return Type.valueOf(mDecryptedMessage[Request.TYPE_INDEX]);
+        }
+
+        /**
+         * Gets verification data of this handshake request.
+         * Currently, we use Provider's BLE address.
+         */
+        public byte[] getVerificationData() {
+            return Arrays.copyOfRange(
+                    mDecryptedMessage,
+                    Request.VERIFICATION_DATA_INDEX,
+                    Request.VERIFICATION_DATA_INDEX + Request.VERIFICATION_DATA_LENGTH);
+        }
+
+        /** Gets Seeker's public address of the handshake request. */
+        public byte[] getSeekerPublicAddress() {
+            return Arrays.copyOfRange(
+                    mDecryptedMessage,
+                    Request.SEEKER_PUBLIC_ADDRESS_INDEX,
+                    Request.SEEKER_PUBLIC_ADDRESS_INDEX + BLUETOOTH_ADDRESS_LENGTH);
+        }
+
+        /** Checks whether the Seeker request discoverability from flags byte. */
+        public boolean requestDiscoverable() {
+            return (getFlags() & REQUEST_DISCOVERABLE) != 0;
+        }
+
+        /**
+         * Checks whether the Seeker requests that the Provider shall initiate bonding from
+         * flags byte.
+         */
+        public boolean requestProviderInitialBonding() {
+            return (getFlags() & PROVIDER_INITIATES_BONDING) != 0;
+        }
+
+        /** Checks whether the Seeker requests that the Provider shall notify the existing name. */
+        public boolean requestDeviceName() {
+            return (getFlags() & REQUEST_DEVICE_NAME) != 0;
+        }
+
+        /** Checks whether this is for retroactively writing account key. */
+        public boolean requestRetroactivePair() {
+            return (getFlags() & REQUEST_RETROACTIVE_PAIR) != 0;
+        }
+
+        /** Gets the flags of this handshake request. */
+        private byte getFlags() {
+            return mDecryptedMessage[Request.FLAGS_INDEX];
+        }
+
+        /** Checks whether the Seeker requests a device action. */
+        public boolean requestDeviceAction() {
+            return (getFlags() & DEVICE_ACTION) != 0;
+        }
+
+        /**
+         * Checks whether the Seeker requests an action which will be followed by an additional
+         * data.
+         */
+        public boolean requestFollowedByAdditionalData() {
+            return (getFlags() & ADDITIONAL_DATA_CHARACTERISTIC) != 0;
+        }
+
+        /** Gets the {@link AdditionalDataType} of this handshake request. */
+        @AdditionalDataType
+        public int getAdditionalDataType() {
+            if (!requestFollowedByAdditionalData()
+                    || mDecryptedMessage.length <= ADDITIONAL_DATA_TYPE_INDEX) {
+                return AdditionalDataType.UNKNOWN;
+            }
+            return mDecryptedMessage[ADDITIONAL_DATA_TYPE_INDEX];
+        }
+
+        /** Enumerates the handshake message types. */
+        public enum Type {
+            KEY_BASED_PAIRING_REQUEST(Request.TYPE_KEY_BASED_PAIRING_REQUEST),
+            ACTION_OVER_BLE(Request.TYPE_ACTION_OVER_BLE),
+            UNKNOWN((byte) 0xFF);
+
+            private final byte mValue;
+
+            Type(byte type) {
+                mValue = type;
+            }
+
+            public static Type valueOf(byte value) {
+                for (Type type : Type.values()) {
+                    if (type.getValue() == value) {
+                        return type;
+                    }
+                }
+                return UNKNOWN;
+            }
+
+            public byte getValue() {
+                return mValue;
+            }
+        }
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPieceTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPieceTest.java
index 670b2ca..32e62a3 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPieceTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HeadsetPieceTest.java
@@ -196,5 +196,63 @@
 
         assertThat(headsetPiece.isBatteryLow()).isFalse();
     }
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void describeContents() {
+        HeadsetPiece headsetPiece =
+                HeadsetPiece.builder()
+                        .setLowLevelThreshold(30)
+                        .setBatteryLevel(18)
+                        .setImageUrl("http://fake.image.path/image.png")
+                        .setCharging(true)
+                        .build();
+        assertThat(headsetPiece.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void hashcode() {
+        HeadsetPiece headsetPiece =
+                HeadsetPiece.builder()
+                        .setLowLevelThreshold(30)
+                        .setBatteryLevel(18)
+                        .setImageUrl("http://fake.image.path/image.png")
+                        .setCharging(true)
+                        .build();
+        HeadsetPiece headsetPiece1 =
+                HeadsetPiece.builder()
+                        .setLowLevelThreshold(30)
+                        .setBatteryLevel(18)
+                        .setImageUrl("http://fake.image.path/image.png")
+                        .setCharging(true)
+                        .build();
+        assertThat(headsetPiece.hashCode()).isEqualTo(headsetPiece1.hashCode());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testToString() {
+        HeadsetPiece headsetPiece =
+                HeadsetPiece.builder()
+                        .setLowLevelThreshold(30)
+                        .setBatteryLevel(18)
+                        .setImageUrl("http://fake.image.path/image.png")
+                        .setCharging(true)
+                        .build();
+        assertThat(headsetPiece.toString())
+                .isEqualTo("HeadsetPiece{lowLevelThreshold=30,"
+                        + " batteryLevel=18,"
+                        + " imageUrl=http://fake.image.path/image.png,"
+                        + " charging=true,"
+                        + " imageContentUri=null}");
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreatorNewArray() {
+        HeadsetPiece[]  headsetPieces =
+                HeadsetPiece.CREATOR.newArray(2);
+        assertThat(headsetPieces.length).isEqualTo(2);
+    }
 }
 
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java
new file mode 100644
index 0000000..81a5d92
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link Ltv}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LtvTest {
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testParseEmpty_throwsException() throws Ltv.ParseException {
+        assertThrows(Ltv.ParseException.class,
+                () -> Ltv.parse(new byte[]{0}));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testParse_finishesSuccessfully() throws Ltv.ParseException {
+        assertThat(Ltv.parse(new byte[]{3, 4, 5, 6})).isNotEmpty();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListenerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListenerTest.java
new file mode 100644
index 0000000..a58a92d
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PairingProgressListenerTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class PairingProgressListenerTest {
+
+    @Test
+    public void testFromOrdinal() {
+        assertThat(PairingProgressListener.fromOrdinal(0)).isEqualTo(
+                PairingProgressListener.PairingEvent.START);
+        assertThat(PairingProgressListener.fromOrdinal(1)).isEqualTo(
+                PairingProgressListener.PairingEvent.SUCCESS);
+        assertThat(PairingProgressListener.fromOrdinal(2)).isEqualTo(
+                PairingProgressListener.PairingEvent.FAILED);
+        assertThat(PairingProgressListener.fromOrdinal(3)).isEqualTo(
+                PairingProgressListener.PairingEvent.UNKNOWN);
+        assertThat(PairingProgressListener.fromOrdinal(4)).isEqualTo(
+                PairingProgressListener.PairingEvent.UNKNOWN);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PreferencesTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PreferencesTest.java
index b40a5a5..378e3b9 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PreferencesTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/PreferencesTest.java
@@ -1266,12 +1266,38 @@
     public void testExtraLoggingInformation() {
         Preferences prefs =
                 Preferences.builder().setExtraLoggingInformation(FIRST_EXTRA_LOGGING_INFO).build();
-        assertThat(prefs.getExtraLoggingInformation()).isEqualTo(FIRST_EXTRA_LOGGING_INFO);
-        assertThat(prefs.toBuilder().build().getExtraLoggingInformation())
-                .isEqualTo(FIRST_EXTRA_LOGGING_INFO);
-
         Preferences prefs2 =
                 Preferences.builder().setExtraLoggingInformation(SECOND_EXTRA_LOGGING_INFO).build();
+        Preferences prefs3 =
+                Preferences.builder().setExtraLoggingInformation(FIRST_EXTRA_LOGGING_INFO).build();
+
+        assertThat(prefs.getExtraLoggingInformation()).isEqualTo(FIRST_EXTRA_LOGGING_INFO);
         assertThat(prefs2.getExtraLoggingInformation()).isEqualTo(SECOND_EXTRA_LOGGING_INFO);
+        assertThat(prefs.toBuilder().build().getExtraLoggingInformation())
+                .isEqualTo(FIRST_EXTRA_LOGGING_INFO);
+        // Test equal()
+        assertThat(prefs.getExtraLoggingInformation().equals(null)).isFalse();
+        assertThat(prefs.getExtraLoggingInformation()
+                .equals(prefs2.getExtraLoggingInformation())).isFalse();
+        assertThat(prefs.getExtraLoggingInformation()
+                .equals(prefs3.getExtraLoggingInformation())).isTrue();
+        // Test getModelId()
+        assertThat(prefs.getExtraLoggingInformation().getModelId())
+                .isEqualTo(prefs3.getExtraLoggingInformation().getModelId());
+        assertThat(prefs.getExtraLoggingInformation().getModelId())
+                .isNotEqualTo(prefs2.getExtraLoggingInformation().getModelId());
+        // Test hashCode()
+        assertThat(prefs.getExtraLoggingInformation().hashCode())
+                .isEqualTo(prefs3.getExtraLoggingInformation().hashCode());
+        assertThat(prefs.getExtraLoggingInformation().hashCode())
+                .isNotEqualTo(prefs2.getExtraLoggingInformation().hashCode());
+        // Test toBuilder()
+
+        assertThat(prefs.getExtraLoggingInformation()
+                .toBuilder().setModelId("000007").build().getModelId())
+                .isEqualTo("000007");
+        // Test toString()
+        assertThat(prefs.getExtraLoggingInformation().toString())
+                .isEqualTo("ExtraLoggingInformation{modelId=000006}");
     }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/TdsExceptionTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/TdsExceptionTest.java
new file mode 100644
index 0000000..e8b9480
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/TdsExceptionTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.nearby.intdefs.FastPairEventIntDefs;
+
+import org.junit.Test;
+
+public class TdsExceptionTest {
+    @Test
+    public void testGetErrorCode() {
+        TdsException tdsException = new TdsException(
+                FastPairEventIntDefs.BrEdrHandoverErrorCode.BLUETOOTH_MAC_INVALID, "format");
+        assertThat(tdsException.getErrorCode())
+                .isEqualTo(FastPairEventIntDefs.BrEdrHandoverErrorCode.BLUETOOTH_MAC_INVALID);
+
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapterTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapterTest.java
new file mode 100644
index 0000000..a9ab7da
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothAdapterTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link BluetoothAdapter}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothAdapterTest {
+
+    private static final byte[] BYTES = new byte[]{0, 1, 2, 3, 4, 5};
+    private static final String ADDRESS = "00:11:22:33:AA:BB";
+
+    @Mock private android.bluetooth.BluetoothAdapter mBluetoothAdapter;
+    @Mock private android.bluetooth.BluetoothDevice mBluetoothDevice;
+    @Mock private android.bluetooth.le.BluetoothLeAdvertiser mBluetoothLeAdvertiser;
+    @Mock private android.bluetooth.le.BluetoothLeScanner mBluetoothLeScanner;
+
+    BluetoothAdapter mTestabilityBluetoothAdapter;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTestabilityBluetoothAdapter = BluetoothAdapter.wrap(mBluetoothAdapter);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNullAdapter_isNull() {
+        assertThat(BluetoothAdapter.wrap(null)).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mTestabilityBluetoothAdapter).isNotNull();
+        assertThat(mTestabilityBluetoothAdapter.unwrap()).isSameInstanceAs(mBluetoothAdapter);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testDisable_callsWrapped() {
+        when(mBluetoothAdapter.disable()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.disable()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEnable_callsWrapped() {
+        when(mBluetoothAdapter.enable()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.enable()).isTrue();
+        when(mBluetoothAdapter.isEnabled()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.isEnabled()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBluetoothLeAdvertiser_callsWrapped() {
+        when(mBluetoothAdapter.getBluetoothLeAdvertiser()).thenReturn(mBluetoothLeAdvertiser);
+        assertThat(mTestabilityBluetoothAdapter.getBluetoothLeAdvertiser().unwrap())
+                .isSameInstanceAs(mBluetoothLeAdvertiser);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBluetoothLeScanner_callsWrapped() {
+        when(mBluetoothAdapter.getBluetoothLeScanner()).thenReturn(mBluetoothLeScanner);
+        assertThat(mTestabilityBluetoothAdapter.getBluetoothLeScanner().unwrap())
+                .isSameInstanceAs(mBluetoothLeScanner);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBondedDevices_callsWrapped() {
+        when(mBluetoothAdapter.getBondedDevices()).thenReturn(null);
+        assertThat(mTestabilityBluetoothAdapter.getBondedDevices()).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testIsDiscovering_pcallsWrapped() {
+        when(mBluetoothAdapter.isDiscovering()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.isDiscovering()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartDiscovery_callsWrapped() {
+        when(mBluetoothAdapter.startDiscovery()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.startDiscovery()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCancelDiscovery_callsWrapped() {
+        when(mBluetoothAdapter.cancelDiscovery()).thenReturn(true);
+        assertThat(mTestabilityBluetoothAdapter.cancelDiscovery()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetRemoteDeviceBytes_callsWrapped() {
+        when(mBluetoothAdapter.getRemoteDevice(BYTES)).thenReturn(mBluetoothDevice);
+        assertThat(mTestabilityBluetoothAdapter.getRemoteDevice(BYTES).unwrap())
+                .isSameInstanceAs(mBluetoothDevice);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetRemoteDeviceString_callsWrapped() {
+        when(mBluetoothAdapter.getRemoteDevice(ADDRESS)).thenReturn(mBluetoothDevice);
+        assertThat(mTestabilityBluetoothAdapter.getRemoteDevice(ADDRESS).unwrap())
+                .isSameInstanceAs(mBluetoothDevice);
+
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDeviceTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDeviceTest.java
new file mode 100644
index 0000000..d494024
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothDeviceTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.util.UUID;
+
+/**
+ * Unit tests for {@link BluetoothDevice}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothDeviceTest {
+    private static final UUID UUID_CONST = UUID.randomUUID();
+    private static final String ADDRESS = "ADDRESS";
+    private static final String STRING = "STRING";
+
+    @Mock private android.bluetooth.BluetoothDevice mBluetoothDevice;
+    @Mock private android.bluetooth.BluetoothGatt mBluetoothGatt;
+    @Mock private android.bluetooth.BluetoothSocket mBluetoothSocket;
+    @Mock private android.bluetooth.BluetoothClass mBluetoothClass;
+
+    BluetoothDevice mTestabilityBluetoothDevice;
+    BluetoothGattCallback mTestBluetoothGattCallback;
+    Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTestabilityBluetoothDevice = BluetoothDevice.wrap(mBluetoothDevice);
+        mTestBluetoothGattCallback = new TestBluetoothGattCallback();
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mTestabilityBluetoothDevice).isNotNull();
+        assertThat(mTestabilityBluetoothDevice.unwrap()).isSameInstanceAs(mBluetoothDevice);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEquality_asExpected() {
+        assertThat(mTestabilityBluetoothDevice.equals(null)).isFalse();
+        assertThat(mTestabilityBluetoothDevice.equals(mTestabilityBluetoothDevice)).isTrue();
+        assertThat(mTestabilityBluetoothDevice.equals(BluetoothDevice.wrap(mBluetoothDevice)))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testHashCode_asExpected() {
+        assertThat(mTestabilityBluetoothDevice.hashCode())
+                .isEqualTo(BluetoothDevice.wrap(mBluetoothDevice).hashCode());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConnectGattWithThreeParameters_callsWrapped() {
+        when(mBluetoothDevice
+                .connectGatt(mContext, true, mTestBluetoothGattCallback.unwrap()))
+                .thenReturn(mBluetoothGatt);
+        assertThat(mTestabilityBluetoothDevice
+                .connectGatt(mContext, true, mTestBluetoothGattCallback)
+                .unwrap())
+                .isSameInstanceAs(mBluetoothGatt);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConnectGattWithFourParameters_callsWrapped() {
+        when(mBluetoothDevice
+                .connectGatt(mContext, true, mTestBluetoothGattCallback.unwrap(), 1))
+                .thenReturn(mBluetoothGatt);
+        assertThat(mTestabilityBluetoothDevice
+                .connectGatt(mContext, true, mTestBluetoothGattCallback, 1)
+                .unwrap())
+                .isSameInstanceAs(mBluetoothGatt);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreateRfcommSocketToServiceRecord_callsWrapped() throws IOException {
+        when(mBluetoothDevice.createRfcommSocketToServiceRecord(UUID_CONST))
+                .thenReturn(mBluetoothSocket);
+        assertThat(mTestabilityBluetoothDevice.createRfcommSocketToServiceRecord(UUID_CONST))
+                .isSameInstanceAs(mBluetoothSocket);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreateInsecureRfcommSocketToServiceRecord_callsWrapped() throws IOException {
+        when(mBluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID_CONST))
+                .thenReturn(mBluetoothSocket);
+        assertThat(mTestabilityBluetoothDevice
+                .createInsecureRfcommSocketToServiceRecord(UUID_CONST))
+                .isSameInstanceAs(mBluetoothSocket);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetPairingConfirmation_callsWrapped() throws IOException {
+        when(mBluetoothDevice.setPairingConfirmation(true)).thenReturn(true);
+        assertThat(mTestabilityBluetoothDevice.setPairingConfirmation(true)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testFetchUuidsWithSdp_callsWrapped() throws IOException {
+        when(mBluetoothDevice.fetchUuidsWithSdp()).thenReturn(true);
+        assertThat(mTestabilityBluetoothDevice.fetchUuidsWithSdp()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCreateBond_callsWrapped() throws IOException {
+        when(mBluetoothDevice.createBond()).thenReturn(true);
+        assertThat(mTestabilityBluetoothDevice.createBond()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetUuids_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getUuids()).thenReturn(null);
+        assertThat(mTestabilityBluetoothDevice.getUuids()).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBondState_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getBondState()).thenReturn(1);
+        assertThat(mTestabilityBluetoothDevice.getBondState()).isEqualTo(1);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetAddress_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getAddress()).thenReturn(ADDRESS);
+        assertThat(mTestabilityBluetoothDevice.getAddress()).isSameInstanceAs(ADDRESS);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetBluetoothClass_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getBluetoothClass()).thenReturn(mBluetoothClass);
+        assertThat(mTestabilityBluetoothDevice.getBluetoothClass())
+                .isSameInstanceAs(mBluetoothClass);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetType_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getType()).thenReturn(1);
+        assertThat(mTestabilityBluetoothDevice.getType()).isEqualTo(1);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetName_callsWrapped() throws IOException {
+        when(mBluetoothDevice.getName()).thenReturn(STRING);
+        assertThat(mTestabilityBluetoothDevice.getName()).isSameInstanceAs(STRING);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testToString_callsWrapped() {
+        when(mBluetoothDevice.toString()).thenReturn(STRING);
+        assertThat(mTestabilityBluetoothDevice.toString()).isSameInstanceAs(STRING);
+    }
+
+    private static class TestBluetoothGattCallback extends BluetoothGattCallback {}
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallbackTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallbackTest.java
new file mode 100644
index 0000000..26ae6d7
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattCallbackTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link BluetoothGattCallback}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothGattCallbackTest {
+    @Mock private android.bluetooth.BluetoothGatt mBluetoothGatt;
+    @Mock private android.bluetooth.BluetoothGattCharacteristic mBluetoothGattCharacteristic;
+    @Mock private android.bluetooth.BluetoothGattDescriptor mBluetoothGattDescriptor;
+
+    TestBluetoothGattCallback mTestBluetoothGattCallback = new TestBluetoothGattCallback();
+
+    @Test
+    public void testOnConnectionStateChange_notCrash() {
+        mTestBluetoothGattCallback.unwrap()
+                .onConnectionStateChange(mBluetoothGatt, 1, 1);
+    }
+
+    @Test
+    public void testOnServiceDiscovered_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onServicesDiscovered(mBluetoothGatt, 1);
+    }
+
+    @Test
+    public void testOnCharacteristicRead_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onCharacteristicRead(mBluetoothGatt,
+                mBluetoothGattCharacteristic, 1);
+    }
+
+    @Test
+    public void testOnCharacteristicWrite_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onCharacteristicWrite(mBluetoothGatt,
+                mBluetoothGattCharacteristic, 1);
+    }
+
+    @Test
+    public void testOnDescriptionRead_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onDescriptorRead(mBluetoothGatt,
+                mBluetoothGattDescriptor, 1);
+    }
+
+    @Test
+    public void testOnDescriptionWrite_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onDescriptorWrite(mBluetoothGatt,
+                mBluetoothGattDescriptor, 1);
+    }
+
+    @Test
+    public void testOnReadRemoteRssi_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onReadRemoteRssi(mBluetoothGatt, 1, 1);
+    }
+
+    @Test
+    public void testOnReliableWriteCompleted_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onReliableWriteCompleted(mBluetoothGatt, 1);
+    }
+
+    @Test
+    public void testOnMtuChanged_notCrash() {
+        mTestBluetoothGattCallback.unwrap().onMtuChanged(mBluetoothGatt, 1, 1);
+    }
+
+    @Test
+    public void testOnCharacteristicChanged_notCrash() {
+        mTestBluetoothGattCallback.unwrap()
+                .onCharacteristicChanged(mBluetoothGatt, mBluetoothGattCharacteristic);
+    }
+
+    private static class TestBluetoothGattCallback extends BluetoothGattCallback { }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallbackTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallbackTest.java
new file mode 100644
index 0000000..fb99317
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerCallbackTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link BluetoothGattServerCallback}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothGattServerCallbackTest {
+    @Mock
+    private android.bluetooth.BluetoothDevice mBluetoothDevice;
+    @Mock
+    private android.bluetooth.BluetoothGattService mBluetoothGattService;
+    @Mock
+    private android.bluetooth.BluetoothGattCharacteristic mBluetoothGattCharacteristic;
+    @Mock
+    private android.bluetooth.BluetoothGattDescriptor mBluetoothGattDescriptor;
+
+    TestBluetoothGattServerCallback
+            mTestBluetoothGattServerCallback = new TestBluetoothGattServerCallback();
+
+    @Test
+    public void testOnCharacteristicReadRequest_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onCharacteristicReadRequest(
+                mBluetoothDevice, 1, 1, mBluetoothGattCharacteristic);
+    }
+
+    @Test
+    public void testOnCharacteristicWriteRequest_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onCharacteristicWriteRequest(
+                mBluetoothDevice,
+                1,
+                mBluetoothGattCharacteristic,
+                false,
+                true,
+                1,
+                new byte[]{1});
+    }
+
+    @Test
+    public void testOnConnectionStateChange_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onConnectionStateChange(
+                mBluetoothDevice,
+                1,
+                2);
+    }
+
+    @Test
+    public void testOnDescriptorReadRequest_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onDescriptorReadRequest(
+                mBluetoothDevice,
+                1,
+                2, mBluetoothGattDescriptor);
+    }
+
+    @Test
+    public void testOnDescriptorWriteRequest_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onDescriptorWriteRequest(
+                mBluetoothDevice,
+                1,
+                mBluetoothGattDescriptor,
+                false,
+                true,
+                2,
+                new byte[]{1});
+    }
+
+    @Test
+    public void testOnExecuteWrite_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onExecuteWrite(
+                mBluetoothDevice,
+                1,
+                false);
+    }
+
+    @Test
+    public void testOnMtuChanged_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onMtuChanged(
+                mBluetoothDevice,
+                1);
+    }
+
+    @Test
+    public void testOnNotificationSent_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onNotificationSent(
+                mBluetoothDevice,
+                1);
+    }
+
+    @Test
+    public void testOnServiceAdded_notCrash() {
+        mTestBluetoothGattServerCallback.unwrap().onServiceAdded(1, mBluetoothGattService);
+    }
+
+    private static class TestBluetoothGattServerCallback extends BluetoothGattServerCallback { }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerTest.java
new file mode 100644
index 0000000..48283d1
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattServerTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.UUID;
+
+/**
+ * Unit tests for {@link BluetoothGattServer}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothGattServerTest {
+    private static final UUID UUID_CONST = UUID.randomUUID();
+    private static final byte[] BYTES = new byte[]{1, 2, 3};
+
+    @Mock private android.bluetooth.BluetoothDevice mBluetoothDevice;
+    @Mock private android.bluetooth.BluetoothGattServer mBluetoothGattServer;
+    @Mock private android.bluetooth.BluetoothGattService mBluetoothGattService;
+    @Mock private android.bluetooth.BluetoothGattCharacteristic mBluetoothGattCharacteristic;
+
+    BluetoothGattServer mTestabilityBluetoothGattServer;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTestabilityBluetoothGattServer = BluetoothGattServer.wrap(mBluetoothGattServer);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mTestabilityBluetoothGattServer).isNotNull();
+        assertThat(mTestabilityBluetoothGattServer.unwrap()).isSameInstanceAs(mBluetoothGattServer);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConnect_callsWrapped() {
+        when(mBluetoothGattServer
+                .connect(mBluetoothDevice, true))
+                .thenReturn(true);
+        assertThat(mTestabilityBluetoothGattServer
+                .connect(BluetoothDevice.wrap(mBluetoothDevice), true))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testAddService_callsWrapped() {
+        when(mBluetoothGattServer
+                .addService(mBluetoothGattService))
+                .thenReturn(true);
+        assertThat(mTestabilityBluetoothGattServer
+                .addService(mBluetoothGattService))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testClearServices_callsWrapped() {
+        doNothing().when(mBluetoothGattServer).clearServices();
+        mTestabilityBluetoothGattServer.clearServices();
+        verify(mBluetoothGattServer).clearServices();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testClose_callsWrapped() {
+        doNothing().when(mBluetoothGattServer).close();
+        mTestabilityBluetoothGattServer.close();
+        verify(mBluetoothGattServer).close();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testNotifyCharacteristicChanged_callsWrapped() {
+        when(mBluetoothGattServer
+                .notifyCharacteristicChanged(
+                        mBluetoothDevice,
+                        mBluetoothGattCharacteristic,
+                        true))
+                .thenReturn(true);
+        assertThat(mTestabilityBluetoothGattServer
+                .notifyCharacteristicChanged(
+                        BluetoothDevice.wrap(mBluetoothDevice),
+                        mBluetoothGattCharacteristic,
+                        true))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSendResponse_callsWrapped() {
+        when(mBluetoothGattServer.sendResponse(
+                mBluetoothDevice, 1, 1, 1, BYTES)).thenReturn(true);
+        mTestabilityBluetoothGattServer.sendResponse(
+                BluetoothDevice.wrap(mBluetoothDevice), 1, 1, 1, BYTES);
+        verify(mBluetoothGattServer).sendResponse(
+                mBluetoothDevice, 1, 1, 1, BYTES);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testCancelConnection_callsWrapped() {
+        doNothing().when(mBluetoothGattServer).cancelConnection(mBluetoothDevice);
+        mTestabilityBluetoothGattServer.cancelConnection(BluetoothDevice.wrap(mBluetoothDevice));
+        verify(mBluetoothGattServer).cancelConnection(mBluetoothDevice);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetService_callsWrapped() {
+        when(mBluetoothGattServer.getService(UUID_CONST)).thenReturn(null);
+        assertThat(mTestabilityBluetoothGattServer.getService(UUID_CONST)).isNull();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java
new file mode 100644
index 0000000..a03a255
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.UUID;
+
+/**
+ * Unit tests for {@link BluetoothGattWrapper}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothGattWrapperTest {
+    private static final UUID UUID_CONST = UUID.randomUUID();
+    private static final byte[] BYTES = new byte[]{1, 2, 3};
+
+    @Mock private android.bluetooth.BluetoothDevice mBluetoothDevice;
+    @Mock private android.bluetooth.BluetoothGatt mBluetoothGatt;
+    @Mock private android.bluetooth.BluetoothGattService mBluetoothGattService;
+    @Mock private android.bluetooth.BluetoothGattCharacteristic mBluetoothGattCharacteristic;
+    @Mock private android.bluetooth.BluetoothGattDescriptor mBluetoothGattDescriptor;
+
+    BluetoothGattWrapper mBluetoothGattWrapper;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBluetoothGattWrapper = BluetoothGattWrapper.wrap(mBluetoothGatt);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mBluetoothGattWrapper).isNotNull();
+        assertThat(mBluetoothGattWrapper.unwrap()).isSameInstanceAs(mBluetoothGatt);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testEquality_asExpected() {
+        assertThat(mBluetoothGattWrapper.equals(null)).isFalse();
+        assertThat(mBluetoothGattWrapper.equals(mBluetoothGattWrapper)).isTrue();
+        assertThat(mBluetoothGattWrapper.equals(BluetoothGattWrapper.wrap(mBluetoothGatt)))
+                .isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetDevice_callsWrapped() {
+        when(mBluetoothGatt.getDevice()).thenReturn(mBluetoothDevice);
+        assertThat(mBluetoothGattWrapper.getDevice().unwrap()).isSameInstanceAs(mBluetoothDevice);
+    }
+
+    @Test
+    public void testHashCode_asExpected() {
+        assertThat(mBluetoothGattWrapper.hashCode())
+                .isEqualTo(BluetoothGattWrapper.wrap(mBluetoothGatt).hashCode());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetServices_callsWrapped() {
+        when(mBluetoothGatt.getServices()).thenReturn(null);
+        assertThat(mBluetoothGattWrapper.getServices()).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testGetService_callsWrapped() {
+        when(mBluetoothGatt.getService(UUID_CONST)).thenReturn(mBluetoothGattService);
+        assertThat(mBluetoothGattWrapper.getService(UUID_CONST))
+                .isSameInstanceAs(mBluetoothGattService);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testDiscoverServices_callsWrapped() {
+        when(mBluetoothGatt.discoverServices()).thenReturn(true);
+        assertThat(mBluetoothGattWrapper.discoverServices()).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testReadCharacteristic_callsWrapped() {
+        when(mBluetoothGatt.readCharacteristic(mBluetoothGattCharacteristic)).thenReturn(true);
+        assertThat(mBluetoothGattWrapper.readCharacteristic(mBluetoothGattCharacteristic)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWriteCharacteristic_callsWrapped() {
+        when(mBluetoothGatt.writeCharacteristic(mBluetoothGattCharacteristic, BYTES, 1))
+                .thenReturn(1);
+        assertThat(mBluetoothGattWrapper.writeCharacteristic(
+                mBluetoothGattCharacteristic, BYTES, 1)).isEqualTo(1);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testReadDescriptor_callsWrapped() {
+        when(mBluetoothGatt.readDescriptor(mBluetoothGattDescriptor)).thenReturn(false);
+        assertThat(mBluetoothGattWrapper.readDescriptor(mBluetoothGattDescriptor)).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWriteDescriptor_callsWrapped() {
+        when(mBluetoothGatt.writeDescriptor(mBluetoothGattDescriptor, BYTES)).thenReturn(5);
+        assertThat(mBluetoothGattWrapper.writeDescriptor(mBluetoothGattDescriptor, BYTES))
+                .isEqualTo(5);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testReadRemoteRssi_callsWrapped() {
+        when(mBluetoothGatt.readRemoteRssi()).thenReturn(false);
+        assertThat(mBluetoothGattWrapper.readRemoteRssi()).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testRequestConnectionPriority_callsWrapped() {
+        when(mBluetoothGatt.requestConnectionPriority(5)).thenReturn(false);
+        assertThat(mBluetoothGattWrapper.requestConnectionPriority(5)).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testRequestMtu_callsWrapped() {
+        when(mBluetoothGatt.requestMtu(5)).thenReturn(false);
+        assertThat(mBluetoothGattWrapper.requestMtu(5)).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testSetCharacteristicNotification_callsWrapped() {
+        when(mBluetoothGatt.setCharacteristicNotification(mBluetoothGattCharacteristic, true))
+                .thenReturn(false);
+        assertThat(mBluetoothGattWrapper
+                .setCharacteristicNotification(mBluetoothGattCharacteristic, true)).isFalse();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testDisconnect_callsWrapped() {
+        doNothing().when(mBluetoothGatt).disconnect();
+        mBluetoothGattWrapper.disconnect();
+        verify(mBluetoothGatt).disconnect();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testClose_callsWrapped() {
+        doNothing().when(mBluetoothGatt).close();
+        mBluetoothGattWrapper.close();
+        verify(mBluetoothGatt).close();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothAdvertiserTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothAdvertiserTest.java
new file mode 100644
index 0000000..8468ed1
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothAdvertiserTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth.le;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link BluetoothLeAdvertiser}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothAdvertiserTest {
+    @Mock android.bluetooth.le.BluetoothLeAdvertiser mWrappedBluetoothLeAdvertiser;
+    @Mock AdvertiseSettings mAdvertiseSettings;
+    @Mock AdvertiseData mAdvertiseData;
+    @Mock AdvertiseCallback mAdvertiseCallback;
+
+    BluetoothLeAdvertiser mBluetoothLeAdvertiser;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBluetoothLeAdvertiser = BluetoothLeAdvertiser.wrap(mWrappedBluetoothLeAdvertiser);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNullAdapter_isNull() {
+        assertThat(BluetoothLeAdvertiser.wrap(null)).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mWrappedBluetoothLeAdvertiser).isNotNull();
+        assertThat(mBluetoothLeAdvertiser.unwrap()).isSameInstanceAs(mWrappedBluetoothLeAdvertiser);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartAdvertisingThreeParameters_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeAdvertiser)
+                .startAdvertising(mAdvertiseSettings, mAdvertiseData, mAdvertiseCallback);
+        mBluetoothLeAdvertiser
+                .startAdvertising(mAdvertiseSettings, mAdvertiseData, mAdvertiseCallback);
+        verify(mWrappedBluetoothLeAdvertiser).startAdvertising(
+                mAdvertiseSettings, mAdvertiseData, mAdvertiseCallback);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartAdvertisingFourParameters_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeAdvertiser).startAdvertising(
+                mAdvertiseSettings, mAdvertiseData, mAdvertiseData, mAdvertiseCallback);
+        mBluetoothLeAdvertiser.startAdvertising(
+                mAdvertiseSettings, mAdvertiseData, mAdvertiseData, mAdvertiseCallback);
+        verify(mWrappedBluetoothLeAdvertiser).startAdvertising(
+                mAdvertiseSettings, mAdvertiseData, mAdvertiseData, mAdvertiseCallback);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStopAdvertising_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeAdvertiser).stopAdvertising(mAdvertiseCallback);
+        mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
+        verify(mWrappedBluetoothLeAdvertiser).stopAdvertising(mAdvertiseCallback);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScannerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScannerTest.java
new file mode 100644
index 0000000..3fce54f
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/BluetoothLeScannerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth.le;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link BluetoothLeScanner}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothLeScannerTest {
+    @Mock android.bluetooth.le.BluetoothLeScanner mWrappedBluetoothLeScanner;
+    @Mock PendingIntent mPendingIntent;
+    @Mock ScanSettings mScanSettings;
+    @Mock ScanFilter mScanFilter;
+
+    TestScanCallback mTestScanCallback = new TestScanCallback();
+    BluetoothLeScanner mBluetoothLeScanner;
+    ImmutableList<ScanFilter> mImmutableScanFilterList;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBluetoothLeScanner = BluetoothLeScanner.wrap(mWrappedBluetoothLeScanner);
+        mImmutableScanFilterList = ImmutableList.of(mScanFilter);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNullAdapter_isNull() {
+        assertThat(BluetoothLeAdvertiser.wrap(null)).isNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testWrapNonNullAdapter_isNotNull_unWrapSame() {
+        assertThat(mWrappedBluetoothLeScanner).isNotNull();
+        assertThat(mBluetoothLeScanner.unwrap()).isSameInstanceAs(mWrappedBluetoothLeScanner);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartScan_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeScanner).startScan(mTestScanCallback.unwrap());
+        mBluetoothLeScanner.startScan(mTestScanCallback);
+        verify(mWrappedBluetoothLeScanner).startScan(mTestScanCallback.unwrap());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartScanWithFiltersCallback_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeScanner)
+                .startScan(mImmutableScanFilterList, mScanSettings, mTestScanCallback.unwrap());
+        mBluetoothLeScanner.startScan(mImmutableScanFilterList, mScanSettings, mTestScanCallback);
+        verify(mWrappedBluetoothLeScanner)
+                .startScan(mImmutableScanFilterList, mScanSettings, mTestScanCallback.unwrap());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStartScanWithFiltersCallbackIntent_callsWrapped() {
+        when(mWrappedBluetoothLeScanner.startScan(
+                mImmutableScanFilterList, mScanSettings, mPendingIntent)).thenReturn(1);
+        mBluetoothLeScanner.startScan(mImmutableScanFilterList, mScanSettings, mPendingIntent);
+        verify(mWrappedBluetoothLeScanner)
+                .startScan(mImmutableScanFilterList, mScanSettings, mPendingIntent);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStopScan_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeScanner).stopScan(mTestScanCallback.unwrap());
+        mBluetoothLeScanner.stopScan(mTestScanCallback);
+        verify(mWrappedBluetoothLeScanner).stopScan(mTestScanCallback.unwrap());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testStopScanPendingIntent_callsWrapped() {
+        doNothing().when(mWrappedBluetoothLeScanner).stopScan(mPendingIntent);
+        mBluetoothLeScanner.stopScan(mPendingIntent);
+        verify(mWrappedBluetoothLeScanner).stopScan(mPendingIntent);
+    }
+
+    private static class TestScanCallback extends ScanCallback {};
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallbackTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallbackTest.java
new file mode 100644
index 0000000..6d68486
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanCallbackTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth.le;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ScanCallback}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScanCallbackTest {
+    @Mock android.bluetooth.le.ScanResult mScanResult;
+
+    TestScanCallback mTestScanCallback = new TestScanCallback();
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testOnScanFailed_notCrash() {
+        mTestScanCallback.unwrap().onScanFailed(1);
+    }
+
+    @Test
+    public void testOnScanResult_notCrash() {
+        mTestScanCallback.unwrap().onScanResult(1, mScanResult);
+    }
+
+    @Test
+    public void testOnBatchScanResult_notCrash() {
+        mTestScanCallback.unwrap().onBatchScanResults(ImmutableList.of(mScanResult));
+    }
+
+    private static class TestScanCallback extends ScanCallback { }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResultTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResultTest.java
new file mode 100644
index 0000000..255c178
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/le/ScanResultTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.bluetooth.testability.android.bluetooth.le;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ScanResult}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScanResultTest {
+
+    @Mock android.bluetooth.le.ScanResult mWrappedScanResult;
+    @Mock android.bluetooth.le.ScanRecord mScanRecord;
+    @Mock android.bluetooth.BluetoothDevice mBluetoothDevice;
+    ScanResult mScanResult;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mScanResult = ScanResult.wrap(mWrappedScanResult);
+    }
+
+    @Test
+    public void testGetScanRecord_calledWrapped() {
+        when(mWrappedScanResult.getScanRecord()).thenReturn(mScanRecord);
+        assertThat(mScanResult.getScanRecord()).isSameInstanceAs(mScanRecord);
+    }
+
+    @Test
+    public void testGetRssi_calledWrapped() {
+        when(mWrappedScanResult.getRssi()).thenReturn(3);
+        assertThat(mScanResult.getRssi()).isEqualTo(3);
+    }
+
+    @Test
+    public void testGetTimestampNanos_calledWrapped() {
+        when(mWrappedScanResult.getTimestampNanos()).thenReturn(4L);
+        assertThat(mScanResult.getTimestampNanos()).isEqualTo(4L);
+    }
+
+    @Test
+    public void testGetDevice_calledWrapped() {
+        when(mWrappedScanResult.getDevice()).thenReturn(mBluetoothDevice);
+        assertThat(mScanResult.getDevice().unwrap()).isSameInstanceAs(mBluetoothDevice);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtilsTest.java
index 47182c3..7535c06 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/util/BluetoothGattUtilsTest.java
@@ -34,14 +34,12 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
-import java.util.UUID;
 
 /** Unit tests for {@link BluetoothGattUtils}. */
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothGattUtilsTest {
-    private static final UUID TEST_UUID = UUID.randomUUID();
     private static final ImmutableSet<String> GATT_HIDDEN_CONSTANTS = ImmutableSet.of(
             "GATT_WRITE_REQUEST_BUSY", "GATT_WRITE_REQUEST_FAIL", "GATT_WRITE_REQUEST_SUCCESS");
 
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/EventLoopTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/EventLoopTest.java
index 70dcec8..bebb2f2 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/EventLoopTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/EventLoopTest.java
@@ -36,7 +36,6 @@
     @Rule
     public ExpectedException thrown = ExpectedException.none();
 
-    /*
     @Test
     public void remove() {
         mEventLoop.postRunnable(new NumberedRunnable(0));
@@ -44,10 +43,8 @@
         mEventLoop.postRunnable(runnableToAddAndRemove);
         mEventLoop.removeRunnable(runnableToAddAndRemove);
         mEventLoop.postRunnable(new NumberedRunnable(2));
-
-        assertThat(mExecutedRunnables).containsExactly(0, 2);
+        assertThat(mExecutedRunnables).doesNotContain(1);
     }
-    */
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/HandlerEventLoopImplTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/HandlerEventLoopImplTest.java
new file mode 100644
index 0000000..4775456
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/HandlerEventLoopImplTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.eventloop;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HandlerEventLoopImplTest {
+    private static final String TAG = "HandlerEventLoopImplTest";
+    private final HandlerEventLoopImpl mHandlerEventLoopImpl =
+            new HandlerEventLoopImpl(TAG);
+    private final List<Integer> mExecutedRunnables = new ArrayList<>();
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void remove() {
+        mHandlerEventLoopImpl.postRunnable(new NumberedRunnable(0));
+        NumberedRunnable runnableToAddAndRemove = new NumberedRunnable(1);
+        mHandlerEventLoopImpl.postRunnable(runnableToAddAndRemove);
+        mHandlerEventLoopImpl.removeRunnable(runnableToAddAndRemove);
+        mHandlerEventLoopImpl.postRunnable(new NumberedRunnable(2));
+        assertThat(mExecutedRunnables).doesNotContain(1);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void isPosted() {
+        NumberedRunnable runnable = new HandlerEventLoopImplTest.NumberedRunnable(0);
+        mHandlerEventLoopImpl.postRunnableDelayed(runnable, 10 * 1000L);
+        assertThat(mHandlerEventLoopImpl.isPosted(runnable)).isTrue();
+        mHandlerEventLoopImpl.removeRunnable(runnable);
+        assertThat(mHandlerEventLoopImpl.isPosted(runnable)).isFalse();
+
+        // Let a runnable execute, then verify that it's not posted.
+        mHandlerEventLoopImpl.postRunnable(runnable);
+        assertThat(mHandlerEventLoopImpl.isPosted(runnable)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void postAndWaitAfterDestroy() throws InterruptedException {
+        mHandlerEventLoopImpl.destroy();
+        mHandlerEventLoopImpl.postAndWait(new HandlerEventLoopImplTest.NumberedRunnable(0));
+        assertThat(mExecutedRunnables).isEmpty();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void postEmptyQueueRunnable() {
+        mHandlerEventLoopImpl.postEmptyQueueRunnable(
+                new HandlerEventLoopImplTest.NumberedRunnable(0));
+    }
+
+    private class NumberedRunnable extends NamedRunnable {
+        private final int mId;
+
+        private NumberedRunnable(int id) {
+            super("NumberedRunnable:" + id);
+            this.mId = id;
+        }
+
+        @Override
+        public void run() {
+            // Note: when running in robolectric, this is not actually executed on a different
+            // thread, it's executed in the same thread the test runs in, so this is safe.
+            mExecutedRunnables.add(mId);
+        }
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/NamedRunnableTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/NamedRunnableTest.java
new file mode 100644
index 0000000..7005da1
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/eventloop/NamedRunnableTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.eventloop;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class NamedRunnableTest {
+    private static final String TAG = "NamedRunnableTest";
+
+    @Test
+    public void testToString() {
+        assertThat(mNamedRunnable.toString()).isEqualTo("Runnable[" + TAG +  "]");
+    }
+
+    private final NamedRunnable mNamedRunnable = new NamedRunnable(TAG) {
+        @Override
+        public void run() {
+        }
+    };
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/fastpair/IconUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/fastpair/IconUtilsTest.java
new file mode 100644
index 0000000..d39d9cc
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/fastpair/IconUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class IconUtilsTest {
+    private static final int MIN_ICON_SIZE = 16;
+    private static final int DESIRED_ICON_SIZE = 32;
+    @Mock
+    Context mContext;
+
+    @Test
+    public void isIconSizedCorrectly() {
+        // Null bitmap is not sized correctly
+        assertThat(IconUtils.isIconSizeCorrect(null)).isFalse();
+
+        int minIconSize = MIN_ICON_SIZE;
+        int desiredIconSize = DESIRED_ICON_SIZE;
+
+        // Bitmap that is 1x1 pixels is not sized correctly
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+        assertThat(IconUtils.isIconSizeCorrect(icon)).isFalse();
+
+        // Bitmap is categorized as small, and not regular
+        icon = Bitmap.createBitmap(minIconSize + 1,
+                minIconSize + 1, Bitmap.Config.ALPHA_8);
+        assertThat(IconUtils.isIconSizeCorrect(icon)).isTrue();
+        assertThat(IconUtils.isIconSizedSmall(icon)).isTrue();
+        assertThat(IconUtils.isIconSizedRegular(icon)).isFalse();
+
+        // Bitmap is categorized as regular, but not small
+        icon = Bitmap.createBitmap(desiredIconSize + 1,
+                desiredIconSize + 1, Bitmap.Config.ALPHA_8);
+        assertThat(IconUtils.isIconSizeCorrect(icon)).isTrue();
+        assertThat(IconUtils.isIconSizedSmall(icon)).isFalse();
+        assertThat(IconUtils.isIconSizedRegular(icon)).isTrue();
+    }
+
+    @Test
+    public void testAddWhiteCircleBackground() {
+        int minIconSize = MIN_ICON_SIZE;
+        Bitmap icon = Bitmap.createBitmap(minIconSize + 1, minIconSize + 1,
+                Bitmap.Config.ALPHA_8);
+
+        assertThat(
+                IconUtils.isIconSizeCorrect(IconUtils.addWhiteCircleBackground(mContext, icon)))
+                .isTrue();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/locator/LocatorTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/locator/LocatorTest.java
new file mode 100644
index 0000000..c3a4e55
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/locator/LocatorTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.locator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.nearby.common.eventloop.EventLoop;
+import com.android.server.nearby.fastpair.FastPairAdvHandler;
+import com.android.server.nearby.fastpair.FastPairModule;
+import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
+import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+
+import src.com.android.server.nearby.fastpair.testing.MockingLocator;
+
+public class LocatorTest {
+    private Locator mLocator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLocator = src.com.android.server.nearby.fastpair.testing.MockingLocator.withMocksOnly(
+                ApplicationProvider.getApplicationContext());
+        mLocator.bind(new FastPairModule());
+    }
+
+    @Test
+    public void genericConstructor() {
+        assertThat(mLocator.get(FastPairCacheManager.class)).isNotNull();
+        assertThat(mLocator.get(FootprintsDeviceManager.class)).isNotNull();
+        assertThat(mLocator.get(EventLoop.class)).isNotNull();
+        assertThat(mLocator.get(FastPairHalfSheetManager.class)).isNotNull();
+        assertThat(mLocator.get(FastPairAdvHandler.class)).isNotNull();
+        assertThat(mLocator.get(Clock.class)).isNotNull();
+    }
+
+    @Test
+    public void genericDestroy() {
+        mLocator.destroy();
+    }
+
+    @Test
+    public void getOptional() {
+        assertThat(mLocator.getOptional(FastPairModule.class)).isNotNull();
+        mLocator.removeBindingForTest(FastPairModule.class);
+        assertThat(mLocator.getOptional(FastPairModule.class)).isNull();
+    }
+
+    @Test
+    public void getParent() {
+        assertThat(mLocator.getParent()).isNotNull();
+    }
+
+    @Test
+    public void getUnboundErrorMessage() {
+        assertThat(mLocator.getUnboundErrorMessage(FastPairModule.class))
+                .isEqualTo(
+                        "Unbound type: com.android.server.nearby.fastpair.FastPairModule\n"
+                        + "Searched locators:\n" + "android.app.Application ->\n"
+                                + "android.app.Application ->\n" + "android.app.Application");
+    }
+
+    @Test
+    public void getContextForTest() {
+        src.com.android.server.nearby.fastpair.testing.MockingLocator  mockingLocator =
+                new MockingLocator(ApplicationProvider.getApplicationContext(), mLocator);
+        assertThat(mockingLocator.getContextForTest()).isNotNull();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/servicemonitor/PackageWatcherTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/servicemonitor/PackageWatcherTest.java
new file mode 100644
index 0000000..eafc7db
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/servicemonitor/PackageWatcherTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.common.servicemonitor;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Test;
+
+public class PackageWatcherTest {
+    private PackageWatcher mPackageWatcher = new PackageWatcher() {
+        @Override
+        public void onSomePackagesChanged() {
+        }
+    };
+
+    @Test
+    public void getPackageName() {
+        Intent intent = new Intent("Action", null);
+        assertThat(mPackageWatcher.getPackageName(intent)).isNull();
+    }
+
+    @Test
+    public void onReceive() {
+        Intent intent = new Intent(Intent.ACTION_PACKAGES_UNSUSPENDED, null);
+        mPackageWatcher.onReceive(ApplicationProvider.getApplicationContext(), intent);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/FastPairAdvHandlerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/FastPairAdvHandlerTest.java
index 346a961..39ea5a9 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/FastPairAdvHandlerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/FastPairAdvHandlerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.nearby.fastpair;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -24,16 +26,22 @@
 import android.content.Context;
 import android.nearby.FastPairDevice;
 
+import com.android.server.nearby.common.bloomfilter.BloomFilter;
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
 import com.android.server.nearby.fastpair.notification.FastPairNotificationManager;
 import com.android.server.nearby.provider.FastPairDataProvider;
 
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import service.proto.Cache;
+import service.proto.Data;
 import service.proto.Rpcs;
 
 public class FastPairAdvHandlerTest {
@@ -45,11 +53,21 @@
     private FastPairHalfSheetManager mFastPairHalfSheetManager;
     @Mock
     private FastPairNotificationManager mFastPairNotificationManager;
+    @Mock
+    private BloomFilter mBloomFilter;
+    @Mock
+    Cache.StoredDiscoveryItem mStoredDiscoveryItem;
+    @Mock
+    Cache.StoredFastPairItem mStoredFastPairItem;
+    @Mock
+    Data.FastPairDeviceWithAccountKey mFastPairDeviceWithAccountKey;
+    private static final byte[] ACCOUNT_KEY = new byte[] {0, 1, 2};
     private static final String BLUETOOTH_ADDRESS = "AA:BB:CC:DD";
     private static final int CLOSE_RSSI = -80;
     private static final int FAR_AWAY_RSSI = -120;
     private static final int TX_POWER = -70;
     private static final byte[] INITIAL_BYTE_ARRAY = new byte[]{0x01, 0x02, 0x03};
+    private static final byte[] SALT = new byte[]{0x01};
 
     LocatorContextWrapper mLocatorContextWrapper;
     FastPairAdvHandler mFastPairAdvHandler;
@@ -99,7 +117,7 @@
     }
 
     @Test
-    public void testSubsequentBroadcast() {
+    public void testSubsequentBroadcast_notShowHalfSheet() {
         byte[] fastPairRecordWithBloomFilter =
                 new byte[]{
                         (byte) 0x02,
@@ -132,4 +150,44 @@
 
         verify(mFastPairHalfSheetManager, never()).showHalfSheet(any());
     }
+
+    @Test
+    public void testFindRecognizedDevice_bloomFilterNotContains_notFound() {
+        when(mFastPairDeviceWithAccountKey.getAccountKey())
+                .thenReturn(ByteString.copyFrom(ACCOUNT_KEY), ByteString.copyFrom(ACCOUNT_KEY));
+        when(mBloomFilter.possiblyContains(any(byte[].class))).thenReturn(false);
+
+        assertThat(FastPairAdvHandler.findRecognizedDevice(
+                ImmutableList.of(mFastPairDeviceWithAccountKey), mBloomFilter, SALT)).isNull();
+    }
+
+    @Test
+    public void testFindRecognizedDevice_bloomFilterContains_found() {
+        when(mFastPairDeviceWithAccountKey.getAccountKey())
+                .thenReturn(ByteString.copyFrom(ACCOUNT_KEY), ByteString.copyFrom(ACCOUNT_KEY));
+        when(mBloomFilter.possiblyContains(any(byte[].class))).thenReturn(true);
+
+        assertThat(FastPairAdvHandler.findRecognizedDevice(
+                ImmutableList.of(mFastPairDeviceWithAccountKey), mBloomFilter, SALT)).isNotNull();
+    }
+
+    @Test
+    public void testFindRecognizedDeviceFromCachedItem_bloomFilterNotContains_notFound() {
+        when(mStoredFastPairItem.getAccountKey())
+                .thenReturn(ByteString.copyFrom(ACCOUNT_KEY), ByteString.copyFrom(ACCOUNT_KEY));
+        when(mBloomFilter.possiblyContains(any(byte[].class))).thenReturn(false);
+
+        assertThat(FastPairAdvHandler.findRecognizedDeviceFromCachedItem(
+                ImmutableList.of(mStoredFastPairItem), mBloomFilter, SALT)).isNull();
+    }
+
+    @Test
+    public void testFindRecognizedDeviceFromCachedItem_bloomFilterContains_found() {
+        when(mStoredFastPairItem.getAccountKey())
+                .thenReturn(ByteString.copyFrom(ACCOUNT_KEY), ByteString.copyFrom(ACCOUNT_KEY));
+        when(mBloomFilter.possiblyContains(any(byte[].class))).thenReturn(true);
+
+        assertThat(FastPairAdvHandler.findRecognizedDeviceFromCachedItem(
+                ImmutableList.of(mStoredFastPairItem), mBloomFilter, SALT)).isNotNull();
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/FlagUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/FlagUtilsTest.java
new file mode 100644
index 0000000..9cf65f4
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/FlagUtilsTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.fastpair;
+
+import org.junit.Test;
+
+public class FlagUtilsTest {
+
+    @Test
+    public void testGetPreferencesBuilder_notCrash() {
+        FlagUtils.getPreferencesBuilder().build();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/DiscoveryItemTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/DiscoveryItemTest.java
new file mode 100644
index 0000000..5d4ea22
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/DiscoveryItemTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.fastpair.cache;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+import com.android.server.nearby.fastpair.FastPairManager;
+import com.android.server.nearby.fastpair.testing.FakeDiscoveryItems;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import service.proto.Cache;
+
+/** Unit tests for {@link DiscoveryItem} */
+public class DiscoveryItemTest {
+    private static final String DEFAULT_MAC_ADDRESS = "00:11:22:33:44:55";
+    private static final String DEFAULT_DESCRIPITON = "description";
+    private static final long DEFAULT_TIMESTAMP = 1000000000L;
+    private static final String DEFAULT_TITLE = "title";
+    private static final String APP_NAME = "app_name";
+    private static final String ACTION_URL =
+            "intent:#Intent;action=com.android.server.nearby:ACTION_FAST_PAIR;"
+            + "package=com.google.android.gms;"
+            + "component=com.google.android.gms/"
+            + ".nearby.discovery.service.DiscoveryService;end";
+    private static final String DISPLAY_URL = "DISPLAY_URL";
+    private static final String TRIGGER_ID = "trigger.id";
+    private static final String FAST_PAIR_ID = "id";
+    private static final int RSSI = -80;
+    private static final int TX_POWER = -10;
+
+    @Mock private Context mContext;
+    private LocatorContextWrapper mLocatorContextWrapper;
+    private FastPairCacheManager mFastPairCacheManager;
+    private FastPairManager mFastPairManager;
+    private DiscoveryItem mDiscoveryItem;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mLocatorContextWrapper = new LocatorContextWrapper(mContext);
+        mFastPairManager = new FastPairManager(mLocatorContextWrapper);
+        mFastPairCacheManager = mLocatorContextWrapper.getLocator().get(FastPairCacheManager.class);
+        when(mContext.getContentResolver()).thenReturn(
+                InstrumentationRegistry.getInstrumentation().getContext().getContentResolver());
+        mDiscoveryItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+    }
+
+    @Test
+    public void testMultipleFields() {
+        assertThat(mDiscoveryItem.getId()).isEqualTo(FAST_PAIR_ID);
+        assertThat(mDiscoveryItem.getDescription()).isEqualTo(DEFAULT_DESCRIPITON);
+        assertThat(mDiscoveryItem.getDisplayUrl()).isEqualTo(DISPLAY_URL);
+        assertThat(mDiscoveryItem.getTriggerId()).isEqualTo(TRIGGER_ID);
+        assertThat(mDiscoveryItem.getMacAddress()).isEqualTo(DEFAULT_MAC_ADDRESS);
+        assertThat(
+                mDiscoveryItem.getFirstObservationTimestampMillis()).isEqualTo(DEFAULT_TIMESTAMP);
+        assertThat(
+                mDiscoveryItem.getLastObservationTimestampMillis()).isEqualTo(DEFAULT_TIMESTAMP);
+        assertThat(mDiscoveryItem.getActionUrl()).isEqualTo(ACTION_URL);
+        assertThat(mDiscoveryItem.getAppName()).isEqualTo(APP_NAME);
+        assertThat(mDiscoveryItem.getRssi()).isEqualTo(RSSI);
+        assertThat(mDiscoveryItem.getTxPower()).isEqualTo(TX_POWER);
+        assertThat(mDiscoveryItem.getFastPairInformation()).isNull();
+        assertThat(mDiscoveryItem.getFastPairSecretKey()).isNull();
+        assertThat(mDiscoveryItem.getIcon()).isNull();
+        assertThat(mDiscoveryItem.getIconFifeUrl()).isNotNull();
+        assertThat(mDiscoveryItem.getState()).isNotNull();
+        assertThat(mDiscoveryItem.getTitle()).isNotNull();
+        assertThat(mDiscoveryItem.isApp()).isFalse();
+        assertThat(mDiscoveryItem.isDeletable(
+                100000L, 0L)).isTrue();
+        assertThat(mDiscoveryItem.isDeviceType(Cache.NearbyType.NEARBY_CHROMECAST)).isTrue();
+        assertThat(mDiscoveryItem.isExpired(
+                100000L, 0L)).isTrue();
+        assertThat(mDiscoveryItem.isFastPair()).isTrue();
+        assertThat(mDiscoveryItem.isPendingAppInstallValid(5)).isTrue();
+        assertThat(mDiscoveryItem.isPendingAppInstallValid(5,
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID,  null,
+                TRIGGER_ID,  DEFAULT_MAC_ADDRESS,  "", RSSI, TX_POWER))).isTrue();
+        assertThat(mDiscoveryItem.isTypeEnabled(Cache.NearbyType.NEARBY_CHROMECAST)).isTrue();
+        assertThat(mDiscoveryItem.toString()).isNotNull();
+    }
+
+    @Test
+    public void isMuted() {
+        assertThat(mDiscoveryItem.isMuted()).isFalse();
+    }
+
+    @Test
+    public void itemWithDefaultDescription_shouldShowUp() {
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+
+        // Null description should not show up.
+        mDiscoveryItem.setStoredItemForTest(DiscoveryItem.newStoredDiscoveryItem());
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID,  null,
+                        TRIGGER_ID,  DEFAULT_MAC_ADDRESS,  "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+
+        // Empty description should not show up.
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID,  "",
+                        TRIGGER_ID,  DEFAULT_MAC_ADDRESS, DEFAULT_TITLE, RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+    }
+
+    @Test
+    public void itemWithEmptyTitle_shouldNotShowUp() {
+        // Null title should not show up.
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+        // Empty title should not show up.
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, DEFAULT_MAC_ADDRESS, "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+
+        // Null title should not show up.
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, DEFAULT_MAC_ADDRESS, null, RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.isReadyForDisplay()).isFalse();
+    }
+
+    @Test
+    public void itemWithRssiAndTxPower_shouldHaveCorrectEstimatedDistance() {
+        assertThat(mDiscoveryItem.getEstimatedDistance()).isWithin(0.01).of(28.18);
+    }
+
+    @Test
+    public void itemWithoutRssiOrTxPower_shouldNotHaveEstimatedDistance() {
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, DEFAULT_MAC_ADDRESS, "", 0, 0));
+        assertThat(mDiscoveryItem.getEstimatedDistance()).isWithin(0.01).of(0);
+    }
+
+    @Test
+    public void getUiHashCode_differentAddress_differentHash() {
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "00:11:22:33:44:55", "", RSSI, TX_POWER));
+        DiscoveryItem compareTo =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        compareTo.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "55:44:33:22:11:00", "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.getUiHashCode()).isNotEqualTo(compareTo.getUiHashCode());
+    }
+
+    @Test
+    public void getUiHashCode_sameAddress_sameHash() {
+        mDiscoveryItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "00:11:22:33:44:55", "", RSSI, TX_POWER));
+        DiscoveryItem compareTo =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        compareTo.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "00:11:22:33:44:55", "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.getUiHashCode()).isEqualTo(compareTo.getUiHashCode());
+    }
+
+    @Test
+    public void isFastPair() {
+        DiscoveryItem fastPairItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        assertThat(fastPairItem.isFastPair()).isTrue();
+    }
+
+    @Test
+    public void testEqual() {
+        DiscoveryItem fastPairItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        assertThat(mDiscoveryItem.equals(fastPairItem)).isTrue();
+    }
+
+    @Test
+    public void testCompareTo() {
+        DiscoveryItem fastPairItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        assertThat(mDiscoveryItem.compareTo(fastPairItem)).isEqualTo(0);
+    }
+
+
+    @Test
+    public void testCopyOfStoredItem() {
+        DiscoveryItem fastPairItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        fastPairItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "00:11:22:33:44:55", "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.equals(fastPairItem)).isFalse();
+        fastPairItem.setStoredItemForTest(mDiscoveryItem.getCopyOfStoredItem());
+        assertThat(mDiscoveryItem.equals(fastPairItem)).isTrue();
+    }
+
+    @Test
+    public void testStoredItemForTest() {
+        DiscoveryItem fastPairItem =
+                FakeDiscoveryItems.newFastPairDiscoveryItem(mLocatorContextWrapper);
+        fastPairItem.setStoredItemForTest(
+                FakeDiscoveryItems.newFastPairDeviceStoredItem(FAST_PAIR_ID, DEFAULT_DESCRIPITON,
+                        TRIGGER_ID, "00:11:22:33:44:55", "", RSSI, TX_POWER));
+        assertThat(mDiscoveryItem.equals(fastPairItem)).isFalse();
+        fastPairItem.setStoredItemForTest(mDiscoveryItem.getStoredItemForTest());
+        assertThat(mDiscoveryItem.equals(fastPairItem)).isTrue();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
index adae97d..18f2cf6 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairCacheManagerTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.le.ScanResult;
 import android.content.Context;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -50,6 +51,10 @@
     DiscoveryItem mDiscoveryItem2;
     @Mock
     Cache.StoredFastPairItem mStoredFastPairItem;
+    @Mock
+    ScanResult mScanResult;
+
+    Context mContext;
     Cache.StoredDiscoveryItem mStoredDiscoveryItem = Cache.StoredDiscoveryItem.newBuilder()
             .setTriggerId(MODEL_ID)
             .setAppName(APP_NAME).build();
@@ -60,12 +65,12 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void notSaveRetrieveInfo() {
-        Context mContext = ApplicationProvider.getApplicationContext();
         when(mDiscoveryItem.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem);
         when(mDiscoveryItem.getTriggerId()).thenReturn(MODEL_ID);
 
@@ -78,20 +83,6 @@
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void saveRetrieveInfo() {
-        Context mContext = ApplicationProvider.getApplicationContext();
-        when(mDiscoveryItem.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem);
-        when(mDiscoveryItem.getTriggerId()).thenReturn(MODEL_ID);
-
-        FastPairCacheManager fastPairCacheManager = new FastPairCacheManager(mContext);
-        fastPairCacheManager.saveDiscoveryItem(mDiscoveryItem);
-        assertThat(fastPairCacheManager.getStoredDiscoveryItem(MODEL_ID).getAppName())
-                .isEqualTo(APP_NAME);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void getAllInfo() {
-        Context mContext = ApplicationProvider.getApplicationContext();
         when(mDiscoveryItem.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem);
         when(mDiscoveryItem.getTriggerId()).thenReturn(MODEL_ID);
         when(mDiscoveryItem2.getCopyOfStoredItem()).thenReturn(mStoredDiscoveryItem2);
@@ -99,18 +90,20 @@
 
         FastPairCacheManager fastPairCacheManager = new FastPairCacheManager(mContext);
         fastPairCacheManager.saveDiscoveryItem(mDiscoveryItem);
-
-        assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(2);
+        assertThat(fastPairCacheManager.getStoredDiscoveryItem(MODEL_ID).getAppName())
+                .isEqualTo(APP_NAME);
+        assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(1);
 
         fastPairCacheManager.saveDiscoveryItem(mDiscoveryItem2);
-
-        assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(3);
+        assertThat(fastPairCacheManager.getStoredDiscoveryItem(MODEL_ID2).getAppName())
+                .isEqualTo(APP_NAME);
+        assertThat(fastPairCacheManager.getAllSavedStoreDiscoveryItem()).hasSize(2);
+        fastPairCacheManager.cleanUp();
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void saveRetrieveInfoStoredFastPairItem() {
-        Context mContext = ApplicationProvider.getApplicationContext();
         Cache.StoredFastPairItem storedFastPairItem = Cache.StoredFastPairItem.newBuilder()
                 .setMacAddress(MAC_ADDRESS)
                 .setAccountKey(ACCOUNT_KEY)
@@ -128,7 +121,6 @@
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
     public void checkGetAllFastPairItems() {
-        Context mContext = ApplicationProvider.getApplicationContext();
         Cache.StoredFastPairItem storedFastPairItem = Cache.StoredFastPairItem.newBuilder()
                 .setMacAddress(MAC_ADDRESS)
                 .setAccountKey(ACCOUNT_KEY)
@@ -149,5 +141,15 @@
 
         assertThat(fastPairCacheManager.getAllSavedStoredFastPairItem().size())
                 .isEqualTo(1);
+
+        fastPairCacheManager.cleanUp();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void getDeviceFromScanResult_notCrash() {
+        FastPairCacheManager fastPairCacheManager = new FastPairCacheManager(mContext);
+        fastPairCacheManager.getDeviceFromScanResult(mScanResult);
+
     }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairDbHelperTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairDbHelperTest.java
new file mode 100644
index 0000000..c5428f5
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/cache/FastPairDbHelperTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.nearby.fastpair.cache;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+public class FastPairDbHelperTest {
+
+    Context mContext;
+    FastPairDbHelper mFastPairDbHelper;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mFastPairDbHelper = new FastPairDbHelper(mContext);
+    }
+
+    @After
+    public void teardown() {
+        mFastPairDbHelper.close();
+    }
+
+    @Test
+    public void testUpgrade_notCrash() {
+        mFastPairDbHelper
+                .onUpgrade(mFastPairDbHelper.getWritableDatabase(), 1, 2);
+    }
+
+    @Test
+    public void testDowngrade_throwsException()  {
+        assertThrows(
+                SQLiteException.class,
+                () -> mFastPairDbHelper.onDowngrade(
+                        mFastPairDbHelper.getWritableDatabase(), 2, 1));
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
index 58e4c47..b51a295 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.nearby.fastpair.halfsheet;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -25,6 +27,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -32,6 +35,8 @@
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import com.android.server.nearby.common.locator.Locator;
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.FastPairController;
@@ -50,13 +55,13 @@
 public class FastPairHalfSheetManagerTest {
     private static final String BLEADDRESS = "11:22:44:66";
     private static final String NAME = "device_name";
+    private static final int PASSKEY = 1234;
     private FastPairHalfSheetManager mFastPairHalfSheetManager;
     private Cache.ScanFastPairStoreItem mScanFastPairStoreItem;
+    @Mock private Context mContext;
     @Mock
     LocatorContextWrapper mContextWrapper;
     @Mock
-    ResolveInfo mResolveInfo;
-    @Mock
     PackageManager mPackageManager;
     @Mock
     Locator mLocator;
@@ -66,7 +71,8 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-
+        when(mContext.getContentResolver()).thenReturn(
+                InstrumentationRegistry.getInstrumentation().getContext().getContentResolver());
         mScanFastPairStoreItem = Cache.ScanFastPairStoreItem.newBuilder()
                 .setAddress(BLEADDRESS)
                 .setDeviceName(NAME)
@@ -133,4 +139,24 @@
         verify(mContextWrapper, never())
                 .startActivityAsUser(intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
     }
+
+    @Test
+    public void getHalfSheetForegroundState() {
+        mFastPairHalfSheetManager =
+                new FastPairHalfSheetManager(mContextWrapper);
+        assertThat(mFastPairHalfSheetManager.getHalfSheetForegroundState()).isTrue();
+    }
+
+    @Test
+    public void testEmptyMethods() {
+        mFastPairHalfSheetManager =
+                new FastPairHalfSheetManager(mContextWrapper);
+        mFastPairHalfSheetManager.destroyBluetoothPairController();
+        mFastPairHalfSheetManager.disableDismissRunnable();
+        mFastPairHalfSheetManager.notifyPairingProcessDone(true, BLEADDRESS, null);
+        mFastPairHalfSheetManager.showPairingFailed();
+        mFastPairHalfSheetManager.showPairingHalfSheet(null);
+        mFastPairHalfSheetManager.showPairingSuccessHalfSheet(BLEADDRESS);
+        mFastPairHalfSheetManager.showPasskeyConfirmation(null, PASSKEY);
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/notification/FastPairNotificationManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/notification/FastPairNotificationManagerTest.java
new file mode 100644
index 0000000..4fb6b37
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/notification/FastPairNotificationManagerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.fastpair.notification;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class FastPairNotificationManagerTest {
+
+    @Mock private Context mContext;
+    private static final boolean USE_LARGE_ICON = true;
+    private static final int NOTIFICATION_ID = 1;
+    private static final String COMPANION_APP = "companionApp";
+    private static final int BATTERY_LEVEL = 1;
+    private static final String DEVICE_NAME = "deviceName";
+    private static final String ADDRESS = "address";
+    private FastPairNotificationManager mFastPairNotificationManager;
+    private LocatorContextWrapper mLocatorContextWrapper;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mLocatorContextWrapper = new LocatorContextWrapper(mContext);
+        when(mContext.getContentResolver()).thenReturn(
+                InstrumentationRegistry.getInstrumentation().getContext().getContentResolver());
+        mFastPairNotificationManager =
+                new FastPairNotificationManager(mLocatorContextWrapper, null,
+                        USE_LARGE_ICON, NOTIFICATION_ID);
+    }
+
+    @Test
+    public void  notifyPairingProcessDone() {
+        mFastPairNotificationManager.notifyPairingProcessDone(true, true,
+                "privateAddress", "publicAddress");
+    }
+
+    @Test
+    public void  showConnectingNotification() {
+        mFastPairNotificationManager.showConnectingNotification();
+    }
+
+    @Test
+    public void   showPairingFailedNotification() {
+        mFastPairNotificationManager.showPairingFailedNotification(new byte[]{1});
+    }
+
+    @Test
+    public void  showPairingSucceededNotification() {
+        mFastPairNotificationManager.showPairingSucceededNotification(COMPANION_APP,
+                BATTERY_LEVEL, DEVICE_NAME, ADDRESS);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandlerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandlerTest.java
new file mode 100644
index 0000000..bfe009c
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/HalfSheetPairingProgressHandlerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.fastpair.pairinghandler;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
+import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection;
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+import com.android.server.nearby.fastpair.cache.DiscoveryItem;
+import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
+import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
+import com.android.server.nearby.fastpair.testing.FakeDiscoveryItems;
+
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+
+import service.proto.Cache;
+import service.proto.Rpcs;
+
+public class HalfSheetPairingProgressHandlerTest {
+    @Mock
+    Locator mLocator;
+    @Mock
+    LocatorContextWrapper mContextWrapper;
+    @Mock
+    Clock mClock;
+    @Mock
+    FastPairCacheManager mFastPairCacheManager;
+    @Mock
+    FastPairConnection mFastPairConnection;
+    @Mock
+    FootprintsDeviceManager mFootprintsDeviceManager;
+
+    private static final String MAC_ADDRESS = "00:11:22:33:44:55";
+    private static final byte[] ACCOUNT_KEY = new byte[]{0x01, 0x02};
+    private static final int SUBSEQUENT_PAIR_START = 1310;
+    private static final int SUBSEQUENT_PAIR_END = 1320;
+    private static final int PASSKEY = 1234;
+    private static HalfSheetPairingProgressHandler sHalfSheetPairingProgressHandler;
+    private static DiscoveryItem sDiscoveryItem;
+    private static BluetoothDevice sBluetoothDevice;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mContextWrapper.getLocator()).thenReturn(mLocator);
+        mLocator.overrideBindingForTest(FastPairCacheManager.class, mFastPairCacheManager);
+        mLocator.overrideBindingForTest(Clock.class, mClock);
+        FastPairHalfSheetManager mfastPairHalfSheetManager =
+                new FastPairHalfSheetManager(mContextWrapper);
+        mLocator.bind(FastPairHalfSheetManager.class, mfastPairHalfSheetManager);
+        when(mLocator.get(FastPairHalfSheetManager.class)).thenReturn(mfastPairHalfSheetManager);
+        sDiscoveryItem = FakeDiscoveryItems.newFastPairDiscoveryItem(mContextWrapper);
+        sDiscoveryItem.setStoredItemForTest(
+                sDiscoveryItem.getStoredItemForTest().toBuilder()
+                        .setAuthenticationPublicKeySecp256R1(ByteString.copyFrom(ACCOUNT_KEY))
+                        .setMacAddress(MAC_ADDRESS)
+                        .setFastPairInformation(
+                                Cache.FastPairInformation.newBuilder()
+                                        .setDeviceType(Rpcs.DeviceType.HEADPHONES).build())
+                        .build());
+        sHalfSheetPairingProgressHandler =
+                new HalfSheetPairingProgressHandler(mContextWrapper, sDiscoveryItem,
+                        sDiscoveryItem.getAppPackageName(), ACCOUNT_KEY);
+
+        sBluetoothDevice =
+                BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:44:55");
+    }
+
+    @Test
+    public void getPairEndEventCode() {
+        assertThat(sHalfSheetPairingProgressHandler
+                .getPairEndEventCode()).isEqualTo(SUBSEQUENT_PAIR_END);
+    }
+
+    @Test
+    public void getPairStartEventCode() {
+        assertThat(sHalfSheetPairingProgressHandler
+                .getPairStartEventCode()).isEqualTo(SUBSEQUENT_PAIR_START);
+    }
+
+    @Test
+    public void testOnHandlePasskeyConfirmation() {
+        sHalfSheetPairingProgressHandler.onHandlePasskeyConfirmation(sBluetoothDevice, PASSKEY);
+    }
+
+    @Test
+    public void testOnPairedCallbackCalled() {
+        sHalfSheetPairingProgressHandler.onPairedCallbackCalled(mFastPairConnection, ACCOUNT_KEY,
+                mFootprintsDeviceManager, MAC_ADDRESS);
+    }
+
+    @Test
+    public void testonPairingFailed() {
+        Throwable e = new Throwable("onPairingFailed");
+        sHalfSheetPairingProgressHandler.onPairingFailed(e);
+    }
+
+    @Test
+    public void testonPairingStarted() {
+        sHalfSheetPairingProgressHandler.onPairingStarted();
+    }
+
+    @Test
+    public void testonPairingSuccess() {
+        sHalfSheetPairingProgressHandler.onPairingSuccess(MAC_ADDRESS);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandlerTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandlerTest.java
new file mode 100644
index 0000000..24f296c
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/NotificationPairingProgressHandlerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.fastpair.pairinghandler;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothManager;
+
+import androidx.annotation.Nullable;
+
+import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection;
+import com.android.server.nearby.common.locator.Locator;
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+import com.android.server.nearby.fastpair.cache.DiscoveryItem;
+import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
+import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
+import com.android.server.nearby.fastpair.notification.FastPairNotificationManager;
+import com.android.server.nearby.fastpair.testing.FakeDiscoveryItems;
+
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+
+import service.proto.Cache;
+import service.proto.Rpcs;
+
+public class NotificationPairingProgressHandlerTest {
+
+    @Mock
+    Locator mLocator;
+    @Mock
+    LocatorContextWrapper mContextWrapper;
+    @Mock
+    Clock mClock;
+    @Mock
+    FastPairCacheManager mFastPairCacheManager;
+    @Mock
+    FastPairConnection mFastPairConnection;
+    @Mock
+    FootprintsDeviceManager mFootprintsDeviceManager;
+    @Mock
+    android.bluetooth.BluetoothManager mBluetoothManager;
+
+    private static final String MAC_ADDRESS = "00:11:22:33:44:55";
+    private static final byte[] ACCOUNT_KEY = new byte[]{0x01, 0x02};
+    private static final int SUBSEQUENT_PAIR_START = 1310;
+    private static final int SUBSEQUENT_PAIR_END = 1320;
+    private static DiscoveryItem sDiscoveryItem;
+    private static  NotificationPairingProgressHandler sNotificationPairingProgressHandler;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mContextWrapper.getSystemService(BluetoothManager.class))
+                .thenReturn(mBluetoothManager);
+        when(mContextWrapper.getLocator()).thenReturn(mLocator);
+        mLocator.overrideBindingForTest(FastPairCacheManager.class,
+                mFastPairCacheManager);
+        mLocator.overrideBindingForTest(Clock.class, mClock);
+        sDiscoveryItem = FakeDiscoveryItems.newFastPairDiscoveryItem(mContextWrapper);
+        sDiscoveryItem.setStoredItemForTest(
+                sDiscoveryItem.getStoredItemForTest().toBuilder()
+                        .setAuthenticationPublicKeySecp256R1(ByteString.copyFrom(ACCOUNT_KEY))
+                        .setFastPairInformation(
+                                Cache.FastPairInformation.newBuilder()
+                                        .setDeviceType(Rpcs.DeviceType.HEADPHONES).build())
+                        .build());
+        sNotificationPairingProgressHandler = createProgressHandler(ACCOUNT_KEY, sDiscoveryItem);
+    }
+
+    @Test
+    public void getPairEndEventCode() {
+        assertThat(sNotificationPairingProgressHandler
+                .getPairEndEventCode()).isEqualTo(SUBSEQUENT_PAIR_END);
+    }
+
+    @Test
+    public void getPairStartEventCode() {
+        assertThat(sNotificationPairingProgressHandler
+                .getPairStartEventCode()).isEqualTo(SUBSEQUENT_PAIR_START);
+    }
+
+    @Test
+    public void onReadyToPair() {
+        sNotificationPairingProgressHandler.onReadyToPair();
+    }
+
+    @Test
+    public void onPairedCallbackCalled() {
+        sNotificationPairingProgressHandler.onPairedCallbackCalled(mFastPairConnection,
+                    ACCOUNT_KEY, mFootprintsDeviceManager, MAC_ADDRESS);
+    }
+
+    @Test
+    public void  onPairingFailed() {
+        Throwable e = new Throwable("Pairing Failed");
+        sNotificationPairingProgressHandler.onPairingFailed(e);
+    }
+
+    @Test
+    public void onPairingSuccess() {
+        sNotificationPairingProgressHandler.onPairingSuccess(sDiscoveryItem.getMacAddress());
+    }
+
+    private NotificationPairingProgressHandler createProgressHandler(
+            @Nullable byte[] accountKey, DiscoveryItem fastPairItem) {
+        FastPairNotificationManager fastPairNotificationManager =
+                new FastPairNotificationManager(mContextWrapper, fastPairItem, true);
+        FastPairHalfSheetManager fastPairHalfSheetManager =
+                new FastPairHalfSheetManager(mContextWrapper);
+        mLocator.overrideBindingForTest(FastPairHalfSheetManager.class, fastPairHalfSheetManager);
+        NotificationPairingProgressHandler mNotificationPairingProgressHandler =
+                new NotificationPairingProgressHandler(
+                        mContextWrapper,
+                        fastPairItem,
+                        fastPairItem.getAppPackageName(),
+                        accountKey,
+                        fastPairNotificationManager);
+        return mNotificationPairingProgressHandler;
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBaseTest.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBaseTest.java
index 2ade5f2..a3eb50c 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBaseTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBaseTest.java
@@ -20,8 +20,14 @@
 
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+
 import androidx.annotation.Nullable;
 
+import com.android.server.nearby.common.bluetooth.fastpair.FastPairConnection;
+import com.android.server.nearby.common.bluetooth.fastpair.Preferences;
 import com.android.server.nearby.common.locator.Locator;
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.cache.DiscoveryItem;
@@ -42,8 +48,8 @@
 
 import service.proto.Cache;
 import service.proto.Rpcs;
-
 public class PairingProgressHandlerBaseTest {
+
     @Mock
     Locator mLocator;
     @Mock
@@ -54,24 +60,46 @@
     FastPairCacheManager mFastPairCacheManager;
     @Mock
     FootprintsDeviceManager mFootprintsDeviceManager;
+    @Mock
+    FastPairConnection mFastPairConnection;
+    @Mock
+    BluetoothManager mBluetoothManager;
+
+    private static final String MAC_ADDRESS = "00:11:22:33:44:55";
     private static final byte[] ACCOUNT_KEY = new byte[]{0x01, 0x02};
+    private static final int PASSKEY = 1234;
+    private static DiscoveryItem sDiscoveryItem;
+    private static PairingProgressHandlerBase sPairingProgressHandlerBase;
+    private static BluetoothDevice sBluetoothDevice;
 
     @Before
     public void setup() {
-
         MockitoAnnotations.initMocks(this);
+        when(mContextWrapper.getSystemService(BluetoothManager.class))
+                .thenReturn(mBluetoothManager);
         when(mContextWrapper.getLocator()).thenReturn(mLocator);
         mLocator.overrideBindingForTest(FastPairCacheManager.class,
                 mFastPairCacheManager);
         mLocator.overrideBindingForTest(Clock.class, mClock);
+        sBluetoothDevice =
+                BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:44:55");
+        sDiscoveryItem = FakeDiscoveryItems.newFastPairDiscoveryItem(mContextWrapper);
+        sDiscoveryItem.setStoredItemForTest(
+                sDiscoveryItem.getStoredItemForTest().toBuilder()
+                        .setAuthenticationPublicKeySecp256R1(ByteString.copyFrom(ACCOUNT_KEY))
+                        .setFastPairInformation(
+                                Cache.FastPairInformation.newBuilder()
+                                        .setDeviceType(Rpcs.DeviceType.HEADPHONES).build())
+                        .build());
+
+        sPairingProgressHandlerBase =
+                createProgressHandler(ACCOUNT_KEY, sDiscoveryItem, /* isRetroactivePair= */ false);
     }
 
     @Test
     public void createHandler_halfSheetSubsequentPairing_notificationPairingHandlerCreated() {
-
-        DiscoveryItem discoveryItem = FakeDiscoveryItems.newFastPairDiscoveryItem(mContextWrapper);
-        discoveryItem.setStoredItemForTest(
-                discoveryItem.getStoredItemForTest().toBuilder()
+        sDiscoveryItem.setStoredItemForTest(
+                sDiscoveryItem.getStoredItemForTest().toBuilder()
                         .setAuthenticationPublicKeySecp256R1(ByteString.copyFrom(ACCOUNT_KEY))
                         .setFastPairInformation(
                                 Cache.FastPairInformation.newBuilder()
@@ -79,7 +107,7 @@
                         .build());
 
         PairingProgressHandlerBase progressHandler =
-                createProgressHandler(ACCOUNT_KEY, discoveryItem, /* isRetroactivePair= */ false);
+                createProgressHandler(ACCOUNT_KEY, sDiscoveryItem, /* isRetroactivePair= */ false);
 
         assertThat(progressHandler).isInstanceOf(NotificationPairingProgressHandler.class);
     }
@@ -87,20 +115,94 @@
     @Test
     public void createHandler_halfSheetInitialPairing_halfSheetPairingHandlerCreated() {
         // No account key
-        DiscoveryItem discoveryItem = FakeDiscoveryItems.newFastPairDiscoveryItem(mContextWrapper);
-        discoveryItem.setStoredItemForTest(
-                discoveryItem.getStoredItemForTest().toBuilder()
+        sDiscoveryItem.setStoredItemForTest(
+                sDiscoveryItem.getStoredItemForTest().toBuilder()
                         .setFastPairInformation(
                                 Cache.FastPairInformation.newBuilder()
                                         .setDeviceType(Rpcs.DeviceType.HEADPHONES).build())
                         .build());
 
         PairingProgressHandlerBase progressHandler =
-                createProgressHandler(null, discoveryItem, /* isRetroactivePair= */ false);
+                createProgressHandler(null, sDiscoveryItem, /* isRetroactivePair= */ false);
 
         assertThat(progressHandler).isInstanceOf(HalfSheetPairingProgressHandler.class);
     }
 
+    @Test
+    public void onPairingStarted() {
+        sPairingProgressHandlerBase.onPairingStarted();
+    }
+
+    @Test
+    public void onWaitForScreenUnlock() {
+        sPairingProgressHandlerBase.onWaitForScreenUnlock();
+    }
+
+    @Test
+    public void  onScreenUnlocked() {
+        sPairingProgressHandlerBase.onScreenUnlocked();
+    }
+
+    @Test
+    public void onReadyToPair() {
+        sPairingProgressHandlerBase.onReadyToPair();
+    }
+
+    @Test
+    public void  onSetupPreferencesBuilder() {
+        Preferences.Builder prefsBuilder =
+                Preferences.builder()
+                        .setEnableBrEdrHandover(false)
+                        .setIgnoreDiscoveryError(true);
+        sPairingProgressHandlerBase.onSetupPreferencesBuilder(prefsBuilder);
+    }
+
+    @Test
+    public void  onPairingSetupCompleted() {
+        sPairingProgressHandlerBase.onPairingSetupCompleted();
+    }
+
+    @Test
+    public void onHandlePasskeyConfirmation() {
+        sPairingProgressHandlerBase.onHandlePasskeyConfirmation(sBluetoothDevice, PASSKEY);
+    }
+
+    @Test
+    public void getKeyForLocalCache() {
+        FastPairConnection.SharedSecret sharedSecret =
+                FastPairConnection.SharedSecret.create(ACCOUNT_KEY, sDiscoveryItem.getMacAddress());
+        sPairingProgressHandlerBase
+                .getKeyForLocalCache(ACCOUNT_KEY, mFastPairConnection, sharedSecret);
+    }
+
+    @Test
+    public void onPairedCallbackCalled() {
+        sPairingProgressHandlerBase.onPairedCallbackCalled(mFastPairConnection,
+                ACCOUNT_KEY, mFootprintsDeviceManager, MAC_ADDRESS);
+    }
+
+    @Test
+    public void onPairingFailed() {
+        Throwable e = new Throwable("Pairing Failed");
+        sPairingProgressHandlerBase.onPairingFailed(e);
+    }
+
+    @Test
+    public void onPairingSuccess() {
+        sPairingProgressHandlerBase.onPairingSuccess(sDiscoveryItem.getMacAddress());
+    }
+
+    @Test
+    public void  optInFootprintsForInitialPairing() {
+        sPairingProgressHandlerBase.optInFootprintsForInitialPairing(
+                mFootprintsDeviceManager, sDiscoveryItem, ACCOUNT_KEY, null);
+    }
+
+    @Test
+    public void skipWaitingScreenUnlock() {
+        assertThat(sPairingProgressHandlerBase.skipWaitingScreenUnlock()).isFalse();
+    }
+
     private PairingProgressHandlerBase createProgressHandler(
             @Nullable byte[] accountKey, DiscoveryItem fastPairItem, boolean isRetroactivePair) {
         FastPairNotificationManager fastPairNotificationManager =
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/FakeDiscoveryItems.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/FakeDiscoveryItems.java
index c406e47..cdec04d 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/FakeDiscoveryItems.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/FakeDiscoveryItems.java
@@ -22,10 +22,17 @@
 import service.proto.Cache;
 
 public class FakeDiscoveryItems {
-    public static final String DEFAULT_MAC_ADDRESS = "00:11:22:33:44:55";
-    public static final long DEFAULT_TIMESTAMP = 1000000000L;
-    public static final String DEFAULT_DESCRIPITON = "description";
-    public static final String TRIGGER_ID = "trigger.id";
+    private static final String DEFAULT_MAC_ADDRESS = "00:11:22:33:44:55";
+    private static final long DEFAULT_TIMESTAMP = 1000000000L;
+    private static final String DEFAULT_DESCRIPITON = "description";
+    private static final String APP_NAME = "app_name";
+    private static final String ACTION_URL =
+            "intent:#Intent;action=com.android.server.nearby:ACTION_FAST_PAIR;"
+                    + "package=com.google.android.gms;"
+                    + "component=com.google.android.gms/"
+                    + ".nearby.discovery.service.DiscoveryService;end";
+    private static final String DISPLAY_URL = "DISPLAY_URL";
+    private static final String TRIGGER_ID = "trigger.id";
     private static final String FAST_PAIR_ID = "id";
     private static final int RSSI = -80;
     private static final int TX_POWER = -10;
@@ -46,9 +53,36 @@
         item.setMacAddress(DEFAULT_MAC_ADDRESS);
         item.setFirstObservationTimestampMillis(DEFAULT_TIMESTAMP);
         item.setLastObservationTimestampMillis(DEFAULT_TIMESTAMP);
+        item.setActionUrl(ACTION_URL);
+        item.setAppName(APP_NAME);
         item.setRssi(RSSI);
         item.setTxPower(TX_POWER);
+        item.setDisplayUrl(DISPLAY_URL);
         return item.build();
     }
 
+    public static Cache.StoredDiscoveryItem newFastPairDeviceStoredItem(String id,
+            String description, String triggerId, String macAddress, String title,
+            int rssi, int txPower) {
+        Cache.StoredDiscoveryItem.Builder item = Cache.StoredDiscoveryItem.newBuilder();
+        item.setState(Cache.StoredDiscoveryItem.State.STATE_ENABLED);
+        if (id != null) {
+            item.setId(id);
+        }
+        if (description != null) {
+            item.setDescription(description);
+        }
+        if (triggerId != null) {
+            item.setTriggerId(triggerId);
+        }
+        if (macAddress != null) {
+            item.setMacAddress(macAddress);
+        }
+        if (title != null) {
+            item.setTitle(title);
+        }
+        item.setRssi(rssi);
+        item.setTxPower(txPower);
+        return item.build();
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/MockingLocator.java b/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/MockingLocator.java
index b261b26..c9a4533 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/MockingLocator.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/fastpair/testing/MockingLocator.java
@@ -41,7 +41,7 @@
     }
 
     @SuppressWarnings("nullness") // due to passing in this before initialized.
-    private MockingLocator(Context context, Locator locator) {
+    public MockingLocator(Context context, Locator locator) {
         super(context, locator);
         this.mLocatorContextWrapper = new LocatorContextWrapper(context, this);
     }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/injector/ContextHubManagerAdapterTest.java b/nearby/tests/unit/src/com/android/server/nearby/injector/ContextHubManagerAdapterTest.java
new file mode 100644
index 0000000..b577064
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/injector/ContextHubManagerAdapterTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.injector;
+
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ContextHubManagerAdapterTest {
+    private ContextHubManagerAdapter mContextHubManagerAdapter;
+
+    @Mock
+    ContextHubManager mContextHubManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContextHubManagerAdapter = new ContextHubManagerAdapter(mContextHubManager);
+    }
+
+    @Test
+    public void getContextHubs() {
+        mContextHubManagerAdapter.getContextHubs();
+    }
+
+    @Test
+    public void queryNanoApps() {
+        mContextHubManagerAdapter.queryNanoApps(new ContextHubInfo());
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/DataElementHeaderTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/DataElementHeaderTest.java
new file mode 100644
index 0000000..e186709
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/DataElementHeaderTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.nearby.BroadcastRequest;
+
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Unit test for {@link DataElementHeader}.
+ */
+public class DataElementHeaderTest {
+
+    private static final int VERSION = BroadcastRequest.PRESENCE_VERSION_V1;
+
+    @Test
+    public void test_illegalLength() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new DataElementHeader(VERSION, 12, 128));
+    }
+
+    @Test
+    public void test_singeByteConversion() {
+        DataElementHeader header = new DataElementHeader(VERSION, 12, 3);
+        byte[] bytes = header.toBytes();
+        assertThat(bytes).isEqualTo(new byte[]{(byte) 0b00111100});
+
+        DataElementHeader afterConversionHeader = DataElementHeader.fromBytes(VERSION, bytes);
+        assertThat(afterConversionHeader.getDataLength()).isEqualTo(3);
+        assertThat(afterConversionHeader.getDataType()).isEqualTo(12);
+    }
+
+    @Test
+    public void test_multipleBytesConversion() {
+        DataElementHeader header = new DataElementHeader(VERSION, 6, 100);
+        DataElementHeader afterConversionHeader =
+                DataElementHeader.fromBytes(VERSION, header.toBytes());
+        assertThat(afterConversionHeader.getDataLength()).isEqualTo(100);
+        assertThat(afterConversionHeader.getDataType()).isEqualTo(6);
+    }
+
+    @Test
+    public void test_fromBytes() {
+        // Single byte case.
+        byte[] singleByte = new byte[]{(byte) 0b01011101};
+        DataElementHeader singeByteHeader = DataElementHeader.fromBytes(VERSION, singleByte);
+        assertThat(singeByteHeader.getDataLength()).isEqualTo(5);
+        assertThat(singeByteHeader.getDataType()).isEqualTo(13);
+
+        // Two bytes case.
+        byte[] twoBytes = new byte[]{(byte) 0b11011101, (byte) 0b01011101};
+        DataElementHeader twoBytesHeader = DataElementHeader.fromBytes(VERSION, twoBytes);
+        assertThat(twoBytesHeader.getDataLength()).isEqualTo(93);
+        assertThat(twoBytesHeader.getDataType()).isEqualTo(93);
+
+        // Three bytes case.
+        byte[] threeBytes = new byte[]{(byte) 0b11011101, (byte) 0b11111111, (byte) 0b01011101};
+        DataElementHeader threeBytesHeader = DataElementHeader.fromBytes(VERSION, threeBytes);
+        assertThat(threeBytesHeader.getDataLength()).isEqualTo(93);
+        assertThat(threeBytesHeader.getDataType()).isEqualTo(16349);
+
+        // Four bytes case.
+        byte[] fourBytes = new byte[]{
+                (byte) 0b11011101, (byte) 0b11111111, (byte) 0b11111111, (byte) 0b01011101};
+
+        DataElementHeader fourBytesHeader = DataElementHeader.fromBytes(VERSION, fourBytes);
+        assertThat(fourBytesHeader.getDataLength()).isEqualTo(93);
+        assertThat(fourBytesHeader.getDataType()).isEqualTo(2097117);
+    }
+
+    @Test
+    public void test_fromBytesIllegal_singleByte() {
+        assertThrows(IllegalArgumentException.class,
+                () -> DataElementHeader.fromBytes(VERSION, new byte[]{(byte) 0b11011101}));
+    }
+
+    @Test
+    public void test_fromBytesIllegal_twoBytes_wrongFirstByte() {
+        assertThrows(IllegalArgumentException.class,
+                () -> DataElementHeader.fromBytes(VERSION,
+                        new byte[]{(byte) 0b01011101, (byte) 0b01011101}));
+    }
+
+    @Test
+    public void test_fromBytesIllegal_twoBytes_wrongLastByte() {
+        assertThrows(IllegalArgumentException.class,
+                () -> DataElementHeader.fromBytes(VERSION,
+                        new byte[]{(byte) 0b11011101, (byte) 0b11011101}));
+    }
+
+    @Test
+    public void test_fromBytesIllegal_threeBytes() {
+        assertThrows(IllegalArgumentException.class,
+                () -> DataElementHeader.fromBytes(VERSION,
+                        new byte[]{(byte) 0b11011101, (byte) 0b11011101, (byte) 0b11011101}));
+    }
+
+    @Test
+    public void test_multipleBytesConversion_largeNumber() {
+        DataElementHeader header = new DataElementHeader(VERSION, 22213546, 66);
+        DataElementHeader afterConversionHeader =
+                DataElementHeader.fromBytes(VERSION, header.toBytes());
+        assertThat(afterConversionHeader.getDataLength()).isEqualTo(66);
+        assertThat(afterConversionHeader.getDataType()).isEqualTo(22213546);
+    }
+
+    @Test
+    public void test_isExtending() {
+        assertThat(DataElementHeader.isExtending((byte) 0b10000100)).isTrue();
+        assertThat(DataElementHeader.isExtending((byte) 0b01110100)).isFalse();
+        assertThat(DataElementHeader.isExtending((byte) 0b00000000)).isFalse();
+    }
+
+    @Test
+    public void test_convertTag() {
+        assertThat(DataElementHeader.convertTag(true)).isEqualTo((byte) 128);
+        assertThat(DataElementHeader.convertTag(false)).isEqualTo(0);
+    }
+
+    @Test
+    public void test_getHeaderValue() {
+        assertThat(DataElementHeader.getHeaderValue((byte) 0b10000100)).isEqualTo(4);
+        assertThat(DataElementHeader.getHeaderValue((byte) 0b00000100)).isEqualTo(4);
+        assertThat(DataElementHeader.getHeaderValue((byte) 0b11010100)).isEqualTo(84);
+        assertThat(DataElementHeader.getHeaderValue((byte) 0b01010100)).isEqualTo(84);
+    }
+
+    @Test
+    public void test_convertTypeMultipleIntList() {
+        List<Byte> list = DataElementHeader.convertTypeMultipleBytes(128);
+        assertThat(list.size()).isEqualTo(2);
+        assertThat(list.get(0)).isEqualTo((byte) 0b10000001);
+        assertThat(list.get(1)).isEqualTo((byte) 0b00000000);
+
+        List<Byte> list2 = DataElementHeader.convertTypeMultipleBytes(10);
+        assertThat(list2.size()).isEqualTo(1);
+        assertThat(list2.get(0)).isEqualTo((byte) 0b00001010);
+
+        List<Byte> list3 = DataElementHeader.convertTypeMultipleBytes(5242398);
+        assertThat(list3.size()).isEqualTo(4);
+        assertThat(list3.get(0)).isEqualTo((byte) 0b10000010);
+        assertThat(list3.get(1)).isEqualTo((byte) 0b10111111);
+        assertThat(list3.get(2)).isEqualTo((byte) 0b11111100);
+        assertThat(list3.get(3)).isEqualTo((byte) 0b00011110);
+    }
+
+    @Test
+    public void test_getTypeMultipleBytes() {
+        byte[] inputBytes = new byte[]{(byte) 0b11011000, (byte) 0b10000000, (byte) 0b00001001};
+        // 0b101100000000000001001
+        assertThat(DataElementHeader.getTypeMultipleBytes(inputBytes)).isEqualTo(1441801);
+
+        byte[] inputBytes2 = new byte[]{(byte) 0b00010010};
+        assertThat(DataElementHeader.getTypeMultipleBytes(inputBytes2)).isEqualTo(18);
+
+        byte[] inputBytes3 = new byte[]{(byte) 0b10000001, (byte) 0b00000000};
+        assertThat(DataElementHeader.getTypeMultipleBytes(inputBytes3)).isEqualTo(128);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementTest.java
new file mode 100644
index 0000000..895df69
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nearby.BroadcastRequest;
+import android.nearby.DataElement;
+import android.nearby.PresenceBroadcastRequest;
+import android.nearby.PresenceCredential;
+import android.nearby.PrivateCredential;
+import android.nearby.PublicCredential;
+
+import com.android.server.nearby.util.encryption.CryptorImpIdentityV1;
+import com.android.server.nearby.util.encryption.CryptorImpV1;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ExtendedAdvertisementTest {
+    private static final int IDENTITY_TYPE = PresenceCredential.IDENTITY_TYPE_PRIVATE;
+    private static final int DATA_TYPE_MODEL_ID = 7;
+    private static final int DATA_TYPE_BLE_ADDRESS = 101;
+    private static final int DATA_TYPE_PUBLIC_IDENTITY = 3;
+    private static final byte[] MODE_ID_DATA =
+            new byte[]{2, 1, 30, 2, 10, -16, 6, 22, 44, -2, -86, -69, -52};
+    private static final byte[] BLE_ADDRESS = new byte[]{124, 4, 56, 60, 120, -29, -90};
+    private static final DataElement MODE_ID_ADDRESS_ELEMENT =
+            new DataElement(DATA_TYPE_MODEL_ID, MODE_ID_DATA);
+    private static final DataElement BLE_ADDRESS_ELEMENT =
+            new DataElement(DATA_TYPE_BLE_ADDRESS, BLE_ADDRESS);
+
+    private static final byte[] IDENTITY =
+            new byte[]{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
+    private static final int MEDIUM_TYPE_BLE = 0;
+    private static final byte[] SALT = {2, 3};
+    private static final int PRESENCE_ACTION_1 = 1;
+    private static final int PRESENCE_ACTION_2 = 2;
+
+    private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4};
+    private static final byte[] AUTHENTICITY_KEY =
+            new byte[]{-97, 10, 107, -86, 25, 65, -54, -95, -72, 59, 54, 93, 9, 3, -24, -88};
+    private static final byte[] PUBLIC_KEY =
+            new byte[] {
+                    48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72, -50, 61,
+                    66, 0, 4, -56, -39, -92, 69, 0, 52, 23, 67, 83, -14, 75, 52, -14, -5, -41, 48,
+                    -83, 31, 42, -39, 102, -13, 22, -73, -73, 86, 30, -96, -84, -13, 4, 122, 104,
+                    -65, 64, 91, -109, -45, -35, -56, 55, -79, 47, -85, 27, -96, -119, -82, -80,
+                    123, 41, -119, -25, 1, -112, 112
+            };
+    private static final byte[] ENCRYPTED_METADATA_BYTES =
+            new byte[] {
+                    -44, -25, -95, -124, -7, 90, 116, -8, 7, -120, -23, -22, -106, -44, -19, 61,
+                    -18, 39, 29, 78, 108, -11, -39, 85, -30, 64, -99, 102, 65, 37, -42, 114, -37,
+                    88, -112, 8, -75, -53, 23, -16, -104, 67, 49, 48, -53, 73, -109, 44, -23, -11,
+                    -118, -61, -37, -104, 60, 105, 115, 1, 56, -89, -107, -45, -116, -1, -25, 84,
+                    -19, -128, 81, 11, 92, 77, -58, 82, 122, 123, 31, -87, -57, 70, 23, -81, 7, 2,
+                    -114, -83, 74, 124, -68, -98, 47, 91, 9, 48, -67, 41, -7, -97, 78, 66, -65, 58,
+                    -4, -46, -30, -85, -50, 100, 46, -66, -128, 7, 66, 9, 88, 95, 12, -13, 81, -91,
+            };
+    private static final byte[] METADATA_ENCRYPTION_KEY_TAG =
+            new byte[] {-126, -104, 1, -1, 26, -46, -68, -86};
+    private static final String DEVICE_NAME = "test_device";
+
+    private PresenceBroadcastRequest.Builder mBuilder;
+    private PrivateCredential mPrivateCredential;
+    private PublicCredential mPublicCredential;
+
+    @Before
+    public void setUp() {
+        mPrivateCredential =
+                new PrivateCredential.Builder(SECRET_ID, AUTHENTICITY_KEY, IDENTITY, DEVICE_NAME)
+                        .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE)
+                        .build();
+        mPublicCredential =
+                new PublicCredential.Builder(SECRET_ID, AUTHENTICITY_KEY, PUBLIC_KEY,
+                        ENCRYPTED_METADATA_BYTES, METADATA_ENCRYPTION_KEY_TAG)
+                        .build();
+        mBuilder =
+                new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+                        SALT, mPrivateCredential)
+                        .setVersion(BroadcastRequest.PRESENCE_VERSION_V1)
+                        .addAction(PRESENCE_ACTION_1)
+                        .addAction(PRESENCE_ACTION_2)
+                        .addExtendedProperty(new DataElement(DATA_TYPE_BLE_ADDRESS, BLE_ADDRESS))
+                        .addExtendedProperty(new DataElement(DATA_TYPE_MODEL_ID, MODE_ID_DATA));
+    }
+
+    @Test
+    public void test_createFromRequest() {
+        ExtendedAdvertisement originalAdvertisement = ExtendedAdvertisement.createFromRequest(
+                mBuilder.build());
+
+        assertThat(originalAdvertisement.getActions())
+                .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2);
+        assertThat(originalAdvertisement.getIdentity()).isEqualTo(IDENTITY);
+        assertThat(originalAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE);
+        assertThat(originalAdvertisement.getLength()).isEqualTo(66);
+        assertThat(originalAdvertisement.getVersion()).isEqualTo(
+                BroadcastRequest.PRESENCE_VERSION_V1);
+        assertThat(originalAdvertisement.getSalt()).isEqualTo(SALT);
+        assertThat(originalAdvertisement.getDataElements())
+                .containsExactly(MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT);
+    }
+
+    @Test
+    public void test_createFromRequest_encodeAndDecode() {
+        ExtendedAdvertisement originalAdvertisement = ExtendedAdvertisement.createFromRequest(
+                mBuilder.build());
+
+        byte[] generatedBytes = originalAdvertisement.toBytes();
+
+        ExtendedAdvertisement newAdvertisement =
+                ExtendedAdvertisement.fromBytes(generatedBytes, mPublicCredential);
+
+        assertThat(newAdvertisement.getActions())
+                .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2);
+        assertThat(newAdvertisement.getIdentity()).isEqualTo(IDENTITY);
+        assertThat(newAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE);
+        assertThat(newAdvertisement.getLength()).isEqualTo(66);
+        assertThat(newAdvertisement.getVersion()).isEqualTo(
+                BroadcastRequest.PRESENCE_VERSION_V1);
+        assertThat(newAdvertisement.getSalt()).isEqualTo(SALT);
+        assertThat(newAdvertisement.getDataElements())
+                .containsExactly(MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT);
+    }
+
+    @Test
+    public void test_createFromRequest_invalidParameter() {
+        // invalid version
+        mBuilder.setVersion(BroadcastRequest.PRESENCE_VERSION_V0);
+        assertThat(ExtendedAdvertisement.createFromRequest(mBuilder.build())).isNull();
+
+        // invalid salt
+        PresenceBroadcastRequest.Builder builder =
+                new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+                        new byte[]{1, 2, 3}, mPrivateCredential)
+                        .setVersion(BroadcastRequest.PRESENCE_VERSION_V1)
+                        .addAction(PRESENCE_ACTION_1);
+        assertThat(ExtendedAdvertisement.createFromRequest(builder.build())).isNull();
+
+        // invalid identity
+        PrivateCredential privateCredential =
+                new PrivateCredential.Builder(SECRET_ID,
+                        AUTHENTICITY_KEY, new byte[]{1, 2, 3, 4}, DEVICE_NAME)
+                        .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE)
+                        .build();
+        PresenceBroadcastRequest.Builder builder2 =
+                new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+                        new byte[]{1, 2, 3}, privateCredential)
+                        .setVersion(BroadcastRequest.PRESENCE_VERSION_V1)
+                        .addAction(PRESENCE_ACTION_1);
+        assertThat(ExtendedAdvertisement.createFromRequest(builder2.build())).isNull();
+
+        // empty action
+        PresenceBroadcastRequest.Builder builder3 =
+                new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE),
+                        SALT, mPrivateCredential)
+                        .setVersion(BroadcastRequest.PRESENCE_VERSION_V1);
+        assertThat(ExtendedAdvertisement.createFromRequest(builder3.build())).isNull();
+    }
+
+    @Test
+    public void test_toBytes() {
+        ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build());
+        assertThat(adv.toBytes()).isEqualTo(getExtendedAdvertisementByteArray());
+    }
+
+    @Test
+    public void test_fromBytes() {
+        byte[] originalBytes = getExtendedAdvertisementByteArray();
+        ExtendedAdvertisement adv =
+                ExtendedAdvertisement.fromBytes(originalBytes, mPublicCredential);
+
+        assertThat(adv.getActions())
+                .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2);
+        assertThat(adv.getIdentity()).isEqualTo(IDENTITY);
+        assertThat(adv.getIdentityType()).isEqualTo(IDENTITY_TYPE);
+        assertThat(adv.getLength()).isEqualTo(66);
+        assertThat(adv.getVersion()).isEqualTo(
+                BroadcastRequest.PRESENCE_VERSION_V1);
+        assertThat(adv.getSalt()).isEqualTo(SALT);
+        assertThat(adv.getDataElements())
+                .containsExactly(MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT);
+    }
+
+    @Test
+    public void test_toString() {
+        ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build());
+        assertThat(adv.toString()).isEqualTo("ExtendedAdvertisement:"
+                + "<VERSION: 1, length: 66, dataElementCount: 2, identityType: 1, "
+                + "identity: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], salt: [2, 3],"
+                + " actions: [1, 2]>");
+    }
+
+    @Test
+    public void test_getDataElements_accordingToType() {
+        ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build());
+        List<DataElement> dataElements = new ArrayList<>();
+
+        dataElements.add(BLE_ADDRESS_ELEMENT);
+        assertThat(adv.getDataElements(DATA_TYPE_BLE_ADDRESS)).isEqualTo(dataElements);
+        assertThat(adv.getDataElements(DATA_TYPE_PUBLIC_IDENTITY)).isEmpty();
+    }
+
+    private static byte[] getExtendedAdvertisementByteArray() {
+        ByteBuffer buffer = ByteBuffer.allocate(66);
+        buffer.put((byte) 0b00100000); // Header V1
+        buffer.put((byte) 0b00100000); // Salt header: length 2, type 0
+        // Salt data
+        buffer.put(SALT);
+        // Identity header: length 16, type 1 (private identity)
+        buffer.put(new byte[]{(byte) 0b10010000, (byte) 0b00000001});
+        // Identity data
+        buffer.put(CryptorImpIdentityV1.getInstance().encrypt(IDENTITY, SALT, AUTHENTICITY_KEY));
+
+        ByteBuffer deBuffer = ByteBuffer.allocate(28);
+        // Action1 header: length 1, type 6
+        deBuffer.put(new byte[]{(byte) 0b00010110});
+        // Action1 data
+        deBuffer.put((byte) PRESENCE_ACTION_1);
+        // Action2 header: length 1, type 6
+        deBuffer.put(new byte[]{(byte) 0b00010110});
+        // Action2 data
+        deBuffer.put((byte) PRESENCE_ACTION_2);
+        // Ble address header: length 7, type 102
+        deBuffer.put(new byte[]{(byte) 0b10000111, (byte) 0b01100101});
+        // Ble address data
+        deBuffer.put(BLE_ADDRESS);
+        // model id header: length 13, type 7
+        deBuffer.put(new byte[]{(byte) 0b10001101, (byte) 0b00000111});
+        // model id data
+        deBuffer.put(MODE_ID_DATA);
+
+        byte[] data = deBuffer.array();
+        CryptorImpV1 cryptor = CryptorImpV1.getInstance();
+        buffer.put(cryptor.encrypt(data, SALT, AUTHENTICITY_KEY));
+        buffer.put(cryptor.sign(data, AUTHENTICITY_KEY));
+
+        return buffer.array();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementUtilsTest.java
new file mode 100644
index 0000000..c4fccf7
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/ExtendedAdvertisementUtilsTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.nearby.BroadcastRequest;
+import android.nearby.DataElement;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Unit test for {@link ExtendedAdvertisementUtils}.
+ */
+public class ExtendedAdvertisementUtilsTest {
+    private static final byte[] ADVERTISEMENT1 = new byte[]{0b00100000, 12, 34, 78, 10};
+    private static final byte[] ADVERTISEMENT2 = new byte[]{0b00100000, 0b00100011, 34, 78,
+            (byte) 0b10010000, (byte) 0b00000100,
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+
+    private static final int DATA_TYPE_SALT = 3;
+    private static final int DATA_TYPE_PRIVATE_IDENTITY = 4;
+
+    @Test
+    public void test_constructHeader() {
+        assertThat(ExtendedAdvertisementUtils.constructHeader(1)).isEqualTo(0b100000);
+        assertThat(ExtendedAdvertisementUtils.constructHeader(0)).isEqualTo(0);
+        assertThat(ExtendedAdvertisementUtils.constructHeader(6)).isEqualTo((byte) 0b11000000);
+    }
+
+    @Test
+    public void test_getVersion() {
+        assertThat(ExtendedAdvertisementUtils.getVersion(ADVERTISEMENT1)).isEqualTo(1);
+        byte[] adv = new byte[]{(byte) 0b10111100, 9, 19, 90, 23};
+        assertThat(ExtendedAdvertisementUtils.getVersion(adv)).isEqualTo(5);
+        byte[] adv2 = new byte[]{(byte) 0b10011111, 9, 19, 90, 23};
+        assertThat(ExtendedAdvertisementUtils.getVersion(adv2)).isEqualTo(4);
+    }
+
+    @Test
+    public void test_getDataElementHeader_salt() {
+        byte[] saltHeaderArray = ExtendedAdvertisementUtils.getDataElementHeader(ADVERTISEMENT2, 1);
+        DataElementHeader header = DataElementHeader.fromBytes(
+                BroadcastRequest.PRESENCE_VERSION_V1, saltHeaderArray);
+        assertThat(header.getDataType()).isEqualTo(DATA_TYPE_SALT);
+        assertThat(header.getDataLength()).isEqualTo(ExtendedAdvertisement.SALT_DATA_LENGTH);
+    }
+
+    @Test
+    public void test_getDataElementHeader_identity() {
+        byte[] identityHeaderArray =
+                ExtendedAdvertisementUtils.getDataElementHeader(ADVERTISEMENT2, 4);
+        DataElementHeader header = DataElementHeader.fromBytes(BroadcastRequest.PRESENCE_VERSION_V1,
+                identityHeaderArray);
+        assertThat(header.getDataType()).isEqualTo(DATA_TYPE_PRIVATE_IDENTITY);
+        assertThat(header.getDataLength()).isEqualTo(ExtendedAdvertisement.IDENTITY_DATA_LENGTH);
+    }
+
+    @Test
+    public void test_constructDataElement_salt() {
+        DataElement salt = new DataElement(DATA_TYPE_SALT, new byte[]{13, 14});
+        byte[] saltArray = ExtendedAdvertisementUtils.convertDataElementToBytes(salt);
+        // Data length and salt header length.
+        assertThat(saltArray.length).isEqualTo(ExtendedAdvertisement.SALT_DATA_LENGTH + 1);
+        // Header
+        assertThat(saltArray[0]).isEqualTo((byte) 0b00100011);
+        // Data
+        assertThat(saltArray[1]).isEqualTo((byte) 13);
+        assertThat(saltArray[2]).isEqualTo((byte) 14);
+    }
+
+    @Test
+    public void test_constructDataElement_privateIdentity() {
+        byte[] identityData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+        DataElement identity = new DataElement(DATA_TYPE_PRIVATE_IDENTITY, identityData);
+        byte[] identityArray = ExtendedAdvertisementUtils.convertDataElementToBytes(identity);
+        // Data length and identity header length.
+        assertThat(identityArray.length).isEqualTo(ExtendedAdvertisement.IDENTITY_DATA_LENGTH + 2);
+        // 1st header byte
+        assertThat(identityArray[0]).isEqualTo((byte) 0b10010000);
+        // 2st header byte
+        assertThat(identityArray[1]).isEqualTo((byte) 0b00000100);
+        // Data
+        assertThat(Arrays.copyOfRange(identityArray, 2, identityArray.length))
+                .isEqualTo(identityData);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java
index 5e0ccbe..8e3e068 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/FastAdvertisementTest.java
@@ -75,6 +75,15 @@
         assertThat(originalAdvertisement.getVersion()).isEqualTo(
                 BroadcastRequest.PRESENCE_VERSION_V0);
         assertThat(originalAdvertisement.getSalt()).isEqualTo(SALT);
+        assertThat(originalAdvertisement.getTxPower()).isEqualTo(TX_POWER);
+        assertThat(originalAdvertisement.toString())
+                .isEqualTo("FastAdvertisement:<VERSION: 0, length: 19,"
+                        + " ltvFieldCount: 4,"
+                        + " identityType: 1,"
+                        + " identity: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],"
+                        + " salt: [2, 3],"
+                        + " actions: [123],"
+                        + " txPower: 4");
     }
 
     @Test
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceDiscoveryResultTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceDiscoveryResultTest.java
index 39cab94..856c1a8 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceDiscoveryResultTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceDiscoveryResultTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.nearby.DataElement;
+import android.nearby.NearbyDeviceParcelable;
 import android.nearby.PresenceCredential;
 import android.nearby.PresenceDevice;
 import android.nearby.PresenceScanFilter;
@@ -28,12 +30,15 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Unit tests for {@link PresenceDiscoveryResult}.
  */
 public class PresenceDiscoveryResultTest {
+    private static final int DATA_TYPE_ACCOUNT_KEY = 9;
+    private static final int DATA_TYPE_INTENT = 6;
     private static final int PRESENCE_ACTION = 123;
     private static final int TX_POWER = -1;
     private static final int RSSI = -41;
@@ -43,6 +48,8 @@
     private static final byte[] PUBLIC_KEY = new byte[]{1, 1, 2, 2};
     private static final byte[] ENCRYPTED_METADATA = new byte[]{1, 2, 3, 4, 5};
     private static final byte[] METADATA_ENCRYPTION_KEY_TAG = new byte[]{1, 1, 3, 4, 5};
+    private static final byte[] META_DATA_ENCRYPTION_KEY =
+            new byte[] {-39, -55, 115, 78, -57, 40, 115, 0, -112, 86, -86, 7, -42, 68, 11, 12};
 
     private PresenceDiscoveryResult.Builder mBuilder;
     private PublicCredential mCredential;
@@ -59,18 +66,68 @@
                 .setSalt(SALT)
                 .setTxPower(TX_POWER)
                 .setRssi(RSSI)
+                .setEncryptedIdentityTag(METADATA_ENCRYPTION_KEY_TAG)
                 .addPresenceAction(PRESENCE_ACTION);
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testToDevice() {
-        PresenceDiscoveryResult discoveryResult = mBuilder.build();
-        PresenceDevice presenceDevice = discoveryResult.toPresenceDevice();
+    public void testFromDevice() {
+        NearbyDeviceParcelable.Builder builder = new NearbyDeviceParcelable.Builder();
+        builder.setTxPower(TX_POWER)
+                .setRssi(RSSI)
+                .setEncryptionKeyTag(METADATA_ENCRYPTION_KEY_TAG)
+                .setSalt(SALT)
+                .setPublicCredential(mCredential);
 
-        assertThat(presenceDevice.getRssi()).isEqualTo(RSSI);
-        assertThat(Arrays.equals(presenceDevice.getSalt(), SALT)).isTrue();
-        assertThat(Arrays.equals(presenceDevice.getSecretId(), SECRET_ID)).isTrue();
+        PresenceDiscoveryResult discoveryResult =
+                PresenceDiscoveryResult.fromDevice(builder.build());
+        PresenceScanFilter scanFilter = new PresenceScanFilter.Builder()
+                .setMaxPathLoss(80)
+                .addCredential(mCredential)
+                .build();
+
+        assertThat(discoveryResult.matches(scanFilter)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testFromDevice_presenceDeviceAvailable() {
+        NearbyDeviceParcelable.Builder builder = new NearbyDeviceParcelable.Builder();
+        PresenceDevice presenceDevice =
+                new PresenceDevice.Builder("123", SALT, SECRET_ID, META_DATA_ENCRYPTION_KEY)
+                        .addExtendedProperty(new DataElement(
+                                DATA_TYPE_INTENT, new byte[]{(byte) PRESENCE_ACTION}))
+                        .build();
+        builder.setTxPower(TX_POWER)
+                .setRssi(RSSI)
+                .setEncryptionKeyTag(METADATA_ENCRYPTION_KEY_TAG)
+                .setPresenceDevice(presenceDevice)
+                .setPublicCredential(mCredential);
+
+        PresenceDiscoveryResult discoveryResult =
+                PresenceDiscoveryResult.fromDevice(builder.build());
+        PresenceScanFilter scanFilter = new PresenceScanFilter.Builder()
+                .setMaxPathLoss(80)
+                .addPresenceAction(PRESENCE_ACTION)
+                .addCredential(mCredential)
+                .build();
+
+        assertThat(discoveryResult.matches(scanFilter)).isTrue();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testAccountMatches() {
+        DataElement accountKey = new DataElement(DATA_TYPE_ACCOUNT_KEY, new byte[]{1, 2, 3, 4});
+        mBuilder.addExtendedProperties(List.of(accountKey));
+        PresenceDiscoveryResult discoveryResult = mBuilder.build();
+
+        List<DataElement> extendedProperties = new ArrayList<>();
+        extendedProperties.add(new DataElement(DATA_TYPE_ACCOUNT_KEY, new byte[]{1, 2, 3, 4}));
+        extendedProperties.add(new DataElement(DATA_TYPE_INTENT,
+                new byte[]{(byte) PRESENCE_ACTION}));
+        assertThat(discoveryResult.accountKeyMatches(extendedProperties)).isTrue();
     }
 
     @Test
@@ -86,4 +143,24 @@
         assertThat(discoveryResult.matches(scanFilter)).isTrue();
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void test_notMatches() {
+        PresenceDiscoveryResult.Builder builder = new PresenceDiscoveryResult.Builder()
+                .setPublicCredential(mCredential)
+                .setSalt(SALT)
+                .setTxPower(TX_POWER)
+                .setRssi(RSSI)
+                .setEncryptedIdentityTag(new byte[]{5, 4, 3, 2, 1})
+                .addPresenceAction(PRESENCE_ACTION);
+
+        PresenceScanFilter scanFilter = new PresenceScanFilter.Builder()
+                .setMaxPathLoss(80)
+                .addPresenceAction(PRESENCE_ACTION)
+                .addCredential(mCredential)
+                .build();
+
+        PresenceDiscoveryResult discoveryResult = builder.build();
+        assertThat(discoveryResult.matches(scanFilter)).isFalse();
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
new file mode 100644
index 0000000..9deb1eb
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.presence;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.nearby.DataElement;
+import android.nearby.PresenceDevice;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.nearby.common.locator.LocatorContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class PresenceManagerTest {
+    private static final byte[] IDENTITY =
+            new byte[] {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
+    private static final byte[] SALT = {2, 3};
+    private static final byte[] SECRET_ID =
+            new byte[] {-97, 10, 107, -86, 25, 65, -54, -95, -72, 59, 54, 93, 9, 3, -24, -88};
+
+    @Mock private Context mContext;
+    private LocatorContextWrapper mLocatorContextWrapper;
+    private PresenceManager mPresenceManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mLocatorContextWrapper = new LocatorContextWrapper(mContext);
+        mPresenceManager = new PresenceManager(mLocatorContextWrapper);
+        when(mContext.getContentResolver())
+                .thenReturn(InstrumentationRegistry.getInstrumentation()
+                        .getContext().getContentResolver());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testInit() {
+        mPresenceManager.initiate();
+
+        verify(mContext, times(1)).registerReceiver(any(), any());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testDeviceStatusUpdated() {
+        DataElement dataElement1 = new DataElement(1, new byte[] {1, 2});
+        DataElement dataElement2 = new DataElement(2, new byte[] {-1, -2, 3, 4, 5, 6, 7, 8, 9});
+
+        PresenceDevice presenceDevice =
+                new PresenceDevice.Builder(/* deviceId= */ "deviceId", SALT, SECRET_ID, IDENTITY)
+                        .addExtendedProperty(dataElement1)
+                        .addExtendedProperty(dataElement2)
+                        .build();
+
+        mPresenceManager.mScanCallback.onDiscovered(presenceDevice);
+        mPresenceManager.mScanCallback.onUpdated(presenceDevice);
+        mPresenceManager.mScanCallback.onLost(presenceDevice);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
index d06a785..05b556b 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleBroadcastProviderTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.nearby.provider;
 
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -24,12 +25,14 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertisingSetCallback;
 import android.content.Context;
+import android.hardware.location.ContextHubManager;
 import android.nearby.BroadcastCallback;
+import android.nearby.BroadcastRequest;
 
 import androidx.test.core.app.ApplicationProvider;
 
-import com.android.server.nearby.injector.ContextHubManagerAdapter;
 import com.android.server.nearby.injector.Injector;
 
 import com.google.common.util.concurrent.MoreExecutors;
@@ -59,9 +62,10 @@
     }
 
     @Test
-    public void testOnStatus_success() {
+    public void testOnStatus_success_fastAdv() {
         byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
-        mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
+        mBleBroadcastProvider.start(BroadcastRequest.PRESENCE_VERSION_V0,
+                advertiseBytes, mBroadcastListener);
 
         AdvertiseSettings settings = new AdvertiseSettings.Builder().build();
         mBleBroadcastProvider.onStartSuccess(settings);
@@ -69,15 +73,47 @@
     }
 
     @Test
-    public void testOnStatus_failure() {
+    public void testOnStatus_success_extendedAdv() {
         byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
-        mBleBroadcastProvider.start(advertiseBytes, mBroadcastListener);
+        mBleBroadcastProvider.start(BroadcastRequest.PRESENCE_VERSION_V1,
+                advertiseBytes, mBroadcastListener);
+
+        // advertising set can not be mocked, so we will allow nulls
+        mBleBroadcastProvider.mAdvertisingSetCallback.onAdvertisingSetStarted(null, -30,
+                AdvertisingSetCallback.ADVERTISE_SUCCESS);
+        verify(mBroadcastListener).onStatusChanged(eq(BroadcastCallback.STATUS_OK));
+    }
+
+    @Test
+    public void testOnStatus_failure_fastAdv() {
+        byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
+        mBleBroadcastProvider.start(BroadcastRequest.PRESENCE_VERSION_V0,
+                advertiseBytes, mBroadcastListener);
 
         mBleBroadcastProvider.onStartFailure(BroadcastCallback.STATUS_FAILURE);
         verify(mBroadcastListener, times(1))
                 .onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
     }
 
+    @Test
+    public void testOnStatus_failure_extendedAdv() {
+        byte[] advertiseBytes = new byte[]{1, 2, 3, 4};
+        mBleBroadcastProvider.start(BroadcastRequest.PRESENCE_VERSION_V1,
+                advertiseBytes, mBroadcastListener);
+
+        // advertising set can not be mocked, so we will allow nulls
+        mBleBroadcastProvider.mAdvertisingSetCallback.onAdvertisingSetStarted(null, -30,
+                AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
+        // Can be additional failure if the test device does not support LE Extended Advertising.
+        verify(mBroadcastListener, atLeastOnce())
+                .onStatusChanged(eq(BroadcastCallback.STATUS_FAILURE));
+    }
+
+    @Test
+    public void testStop() {
+        mBleBroadcastProvider.stop();
+    }
+
     private static class TestInjector implements Injector {
 
         @Override
@@ -88,7 +124,7 @@
         }
 
         @Override
-        public ContextHubManagerAdapter getContextHubManagerAdapter() {
+        public ContextHubManager getContextHubManager() {
             return null;
         }
 
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
index 902cc33..9531c53 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BleDiscoveryProviderTest.java
@@ -18,6 +18,8 @@
 
 import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -30,10 +32,13 @@
 import android.bluetooth.le.ScanRecord;
 import android.bluetooth.le.ScanResult;
 import android.content.Context;
+import android.hardware.location.ContextHubManager;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanFilter;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.server.nearby.injector.ContextHubManagerAdapter;
 import com.android.server.nearby.injector.Injector;
 
 import org.junit.Before;
@@ -42,6 +47,8 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 public final class BleDiscoveryProviderTest {
 
@@ -70,6 +77,7 @@
         // Wait for callback to be invoked
         Thread.sleep(500);
         verify(mListener, times(1)).onNearbyDeviceDiscovered(any());
+        mBleDiscoveryProvider.getScanCallback().onScanFailed(1);
     }
 
     @Test
@@ -78,6 +86,22 @@
         mBleDiscoveryProvider.onStop();
     }
 
+    @Test
+    public void test_stopScan_filersReset() {
+        List<ScanFilter> filterList = new ArrayList<>();
+        filterList.add(getSanFilter());
+
+        mBleDiscoveryProvider.getController().setProviderScanFilters(filterList);
+        mBleDiscoveryProvider.onStart();
+        mBleDiscoveryProvider.onStop();
+        assertThat(mBleDiscoveryProvider.mScanFilters).isEmpty();
+    }
+
+    @Test
+    public void testInvalidateScanMode() {
+        mBleDiscoveryProvider.invalidateScanMode();
+    }
+
     private class TestInjector implements Injector {
         @Override
         public BluetoothAdapter getBluetoothAdapter() {
@@ -85,7 +109,7 @@
         }
 
         @Override
-        public ContextHubManagerAdapter getContextHubManagerAdapter() {
+        public ContextHubManager getContextHubManager() {
             return null;
         }
 
@@ -125,4 +149,22 @@
             return null;
         }
     }
+
+    private static PresenceScanFilter getSanFilter() {
+        return new PresenceScanFilter.Builder()
+                .setMaxPathLoss(70)
+                .addCredential(getPublicCredential())
+                .addPresenceAction(124)
+                .build();
+    }
+
+    private static PublicCredential getPublicCredential() {
+        return new PublicCredential.Builder(
+                new byte[]{1, 2},
+                new byte[]{1, 2},
+                new byte[]{1, 2},
+                new byte[]{1, 2},
+                new byte[]{1, 2})
+                .build();
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
index d45d570..0179901 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
@@ -101,8 +101,13 @@
     @Test
     public void testStartAdvertising() {
         mBroadcastProviderManager.startBroadcast(mBroadcastRequest, mBroadcastListener);
-        verify(mBleBroadcastProvider).start(any(byte[].class), any(
-                BleBroadcastProvider.BroadcastListener.class));
+        verify(mBleBroadcastProvider).start(eq(BroadcastRequest.PRESENCE_VERSION_V0),
+                any(byte[].class), any(BleBroadcastProvider.BroadcastListener.class));
+    }
+
+    @Test
+    public void testStopAdvertising() {
+        mBroadcastProviderManager.stopBroadcast(mBroadcastListener);
     }
 
     @Test
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
index 1b29b52..2bc2be3 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreCommunicationTest.java
@@ -16,18 +16,21 @@
 
 package com.android.server.nearby.provider;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.hardware.location.ContextHubClient;
 import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubManager;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.NanoAppMessage;
 import android.hardware.location.NanoAppState;
 
-import com.android.server.nearby.injector.ContextHubManagerAdapter;
 import com.android.server.nearby.injector.Injector;
 
 import org.junit.Before;
@@ -43,7 +46,8 @@
 
 public class ChreCommunicationTest {
     @Mock Injector mInjector;
-    @Mock ContextHubManagerAdapter mManager;
+    @Mock Context mContext;
+    @Mock ContextHubManager mManager;
     @Mock ContextHubTransaction<List<NanoAppState>> mTransaction;
     @Mock ContextHubTransaction.Response<List<NanoAppState>> mTransactionResponse;
     @Mock ContextHubClient mClient;
@@ -57,17 +61,17 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mInjector.getContextHubManagerAdapter()).thenReturn(mManager);
+        when(mInjector.getContextHubManager()).thenReturn(mManager);
         when(mManager.getContextHubs()).thenReturn(Collections.singletonList(new ContextHubInfo()));
         when(mManager.queryNanoApps(any())).thenReturn(mTransaction);
-        when(mManager.createClient(any(), any(), any())).thenReturn(mClient);
+        when(mManager.createClient(any(), any(), any(), any())).thenReturn(mClient);
         when(mTransactionResponse.getResult()).thenReturn(ContextHubTransaction.RESULT_SUCCESS);
         when(mTransactionResponse.getContents())
                 .thenReturn(
                         Collections.singletonList(
                                 new NanoAppState(ChreDiscoveryProvider.NANOAPP_ID, 1, true)));
 
-        mChreCommunication = new ChreCommunication(mInjector, new InlineExecutor());
+        mChreCommunication = new ChreCommunication(mInjector, mContext, new InlineExecutor());
         mChreCommunication.start(
                 mChreCallback, Collections.singleton(ChreDiscoveryProvider.NANOAPP_ID));
 
@@ -106,6 +110,50 @@
                         new byte[] {1, 2, 3});
         mChreCommunication.onMessageFromNanoApp(mClient, message);
         verify(mChreCallback).onMessageFromNanoApp(eq(message));
+
+    }
+
+    @Test
+    public void testContextHubTransactionResultToString() {
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_SUCCESS))
+                .isEqualTo("RESULT_SUCCESS");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_UNKNOWN))
+                .isEqualTo("RESULT_FAILED_UNKNOWN");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_BAD_PARAMS))
+                .isEqualTo("RESULT_FAILED_BAD_PARAMS");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_UNINITIALIZED))
+                .isEqualTo("RESULT_FAILED_UNINITIALIZED");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_BUSY))
+                .isEqualTo("RESULT_FAILED_BUSY");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_AT_HUB))
+                .isEqualTo("RESULT_FAILED_AT_HUB");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_TIMEOUT))
+                .isEqualTo("RESULT_FAILED_TIMEOUT");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE))
+                .isEqualTo("RESULT_FAILED_SERVICE_INTERNAL_FAILURE");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(
+                        ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE))
+                .isEqualTo("RESULT_FAILED_HAL_UNAVAILABLE");
+        assertThat(
+                mChreCommunication.contextHubTransactionResultToString(9))
+                .isEqualTo("UNKNOWN_RESULT value=9");
     }
 
     @Test
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java
index 7c0dd92..270de52 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java
@@ -16,16 +16,21 @@
 
 package com.android.server.nearby.provider;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.hardware.location.NanoAppMessage;
-import android.nearby.ScanFilter;
+import android.nearby.DataElement;
+import android.nearby.NearbyDeviceParcelable;
 
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.server.nearby.presence.PresenceDiscoveryResult;
+
 import com.google.protobuf.ByteString;
 
 import org.junit.Before;
@@ -35,17 +40,25 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import service.proto.Blefilter;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
+import service.proto.Blefilter;
+
 public class ChreDiscoveryProviderTest {
     @Mock AbstractDiscoveryProvider.Listener mListener;
     @Mock ChreCommunication mChreCommunication;
 
     @Captor ArgumentCaptor<ChreCommunication.ContextHubCommsCallback> mChreCallbackCaptor;
+    @Captor ArgumentCaptor<NearbyDeviceParcelable> mNearbyDevice;
+
+    private static final int DATA_TYPE_CONNECTION_STATUS_KEY = 10;
+    private static final int DATA_TYPE_BATTERY_KEY = 11;
+    private static final int DATA_TYPE_TX_POWER_KEY = 5;
+    private static final int DATA_TYPE_BLUETOOTH_ADDR_KEY = 101;
+    private static final int DATA_TYPE_FP_ACCOUNT_KEY = 9;
+    private static final int DATA_TYPE_BLE_SERVICE_DATA_KEY = 100;
 
     private ChreDiscoveryProvider mChreDiscoveryProvider;
 
@@ -59,13 +72,10 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
-    public void testOnStart() {
-        List<ScanFilter> scanFilters = new ArrayList<>();
-        mChreDiscoveryProvider.getController().setProviderScanFilters(scanFilters);
-        mChreDiscoveryProvider.onStart();
+    public void testInit() {
+        mChreDiscoveryProvider.init();
         verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
         mChreCallbackCaptor.getValue().started(true);
-        verify(mChreCommunication).sendMessageToNanoApp(any());
     }
 
     @Test
@@ -93,12 +103,92 @@
                         ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
                         results.toByteArray());
         mChreDiscoveryProvider.getController().setListener(mListener);
+        mChreDiscoveryProvider.init();
         mChreDiscoveryProvider.onStart();
         verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
         mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
         verify(mListener).onNearbyDeviceDiscovered(any());
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testOnNearbyDeviceDiscoveredWithDataElements() {
+        final byte [] connectionStatus = new byte[] {1, 2, 3};
+        final byte [] batteryStatus = new byte[] {4, 5, 6};
+        final byte [] txPower = new byte[] {2};
+        final byte [] bluetoothAddr = new byte[] {1, 2, 3, 4, 5, 6};
+        final byte [] fastPairAccountKey = new byte[16];
+        // First byte is length of service data, padding zeros should be thrown away.
+        final byte [] bleServiceData = new byte[] {5, 1, 2, 3, 4, 5, 0, 0, 0, 0};
+
+        final List<DataElement> expectedExtendedProperties = new ArrayList<>();
+        expectedExtendedProperties.add(new DataElement(DATA_TYPE_CONNECTION_STATUS_KEY,
+                connectionStatus));
+        expectedExtendedProperties.add(new DataElement(DATA_TYPE_BATTERY_KEY, batteryStatus));
+        expectedExtendedProperties.add(new DataElement(DATA_TYPE_TX_POWER_KEY, txPower));
+        expectedExtendedProperties.add(
+                new DataElement(DATA_TYPE_BLUETOOTH_ADDR_KEY, bluetoothAddr));
+        expectedExtendedProperties.add(
+                new DataElement(DATA_TYPE_FP_ACCOUNT_KEY, fastPairAccountKey));
+        expectedExtendedProperties.add(
+                new DataElement(DATA_TYPE_BLE_SERVICE_DATA_KEY, new byte[] {1, 2, 3, 4, 5}));
+
+        Blefilter.PublicCredential credential =
+                Blefilter.PublicCredential.newBuilder()
+                        .setSecretId(ByteString.copyFrom(new byte[] {1}))
+                        .setAuthenticityKey(ByteString.copyFrom(new byte[2]))
+                        .setPublicKey(ByteString.copyFrom(new byte[3]))
+                        .setEncryptedMetadata(ByteString.copyFrom(new byte[4]))
+                        .setEncryptedMetadataTag(ByteString.copyFrom(new byte[5]))
+                        .build();
+        Blefilter.BleFilterResult result =
+                Blefilter.BleFilterResult.newBuilder()
+                        .setTxPower(2)
+                        .setRssi(1)
+                        .setBluetoothAddress(ByteString.copyFrom(bluetoothAddr))
+                        .setBleServiceData(ByteString.copyFrom(bleServiceData))
+                        .setPublicCredential(credential)
+                        .addDataElement(Blefilter.DataElement.newBuilder()
+                                .setKey(
+                                        Blefilter.DataElement.ElementType
+                                                .DE_CONNECTION_STATUS)
+                                .setValue(ByteString.copyFrom(connectionStatus))
+                                .setValueLength(connectionStatus.length)
+                        )
+                        .addDataElement(Blefilter.DataElement.newBuilder()
+                                .setKey(
+                                        Blefilter.DataElement.ElementType
+                                                .DE_BATTERY_STATUS)
+                                .setValue(ByteString.copyFrom(batteryStatus))
+                                .setValueLength(batteryStatus.length)
+                        )
+                        .addDataElement(Blefilter.DataElement.newBuilder()
+                                .setKey(
+                                        Blefilter.DataElement.ElementType
+                                                .DE_FAST_PAIR_ACCOUNT_KEY)
+                                .setValue(ByteString.copyFrom(fastPairAccountKey))
+                                .setValueLength(fastPairAccountKey.length)
+                        )
+                        .build();
+        Blefilter.BleFilterResults results =
+                Blefilter.BleFilterResults.newBuilder().addResult(result).build();
+        NanoAppMessage chre_message =
+                NanoAppMessage.createMessageToNanoApp(
+                        ChreDiscoveryProvider.NANOAPP_ID,
+                        ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
+                        results.toByteArray());
+        mChreDiscoveryProvider.getController().setListener(mListener);
+        mChreDiscoveryProvider.init();
+        mChreDiscoveryProvider.onStart();
+        verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
+        mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
+        verify(mListener).onNearbyDeviceDiscovered(mNearbyDevice.capture());
+
+        List<DataElement> extendedProperties = PresenceDiscoveryResult
+                .fromDevice(mNearbyDevice.getValue()).getExtendedProperties();
+        assertThat(extendedProperties).containsExactlyElementsIn(expectedExtendedProperties);
+    }
+
     private static class InLineExecutor implements Executor {
         @Override
         public void execute(Runnable command) {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java
new file mode 100644
index 0000000..1af959d
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.provider;
+
+import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
+import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
+
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.nearby.NearbyDeviceParcelable;
+import android.nearby.PresenceScanFilter;
+import android.nearby.PublicCredential;
+import android.nearby.ScanFilter;
+
+import com.android.server.nearby.injector.Injector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DiscoveryProviderManagerTest {
+    @Mock Injector mInjector;
+    @Mock Context mContext;
+    @Mock AppOpsManager mAppOpsManager;
+    private DiscoveryProviderManager mDiscoveryProviderManager;
+
+    private static final int RSSI = -60;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
+        mDiscoveryProviderManager =
+                new DiscoveryProviderManager(mContext, mInjector);
+    }
+
+
+    @Test
+    public void testOnNearbyDeviceDiscovered() {
+        NearbyDeviceParcelable nearbyDeviceParcelable = new NearbyDeviceParcelable.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .build();
+        mDiscoveryProviderManager.onNearbyDeviceDiscovered(nearbyDeviceParcelable);
+    }
+
+    @Test
+    public void testInvalidateProviderScanMode() {
+        mDiscoveryProviderManager.invalidateProviderScanMode();
+    }
+
+    @Test
+    public void testStartChreProvider() {
+        mDiscoveryProviderManager.startChreProvider();
+    }
+
+    private static PresenceScanFilter getPresenceScanFilter() {
+        final byte[] secretId = new byte[]{1, 2, 3, 4};
+        final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
+        final byte[] publicKey = new byte[]{1, 1, 2, 2};
+        final byte[] encryptedMetadata = new byte[]{1, 2, 3, 4, 5};
+        final byte[] metadataEncryptionKeyTag = new byte[]{1, 1, 3, 4, 5};
+
+        PublicCredential credential = new PublicCredential.Builder(
+                secretId, authenticityKey, publicKey, encryptedMetadata, metadataEncryptionKeyTag)
+                .setIdentityType(IDENTITY_TYPE_PRIVATE)
+                .build();
+
+        final int action = 123;
+        return new PresenceScanFilter.Builder()
+                .addCredential(credential)
+                .setMaxPathLoss(RSSI)
+                .addPresenceAction(action)
+                .build();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/FastPairDataProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/FastPairDataProviderTest.java
new file mode 100644
index 0000000..300efbd
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/FastPairDataProviderTest.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.nearby.FastPairDataProviderService;
+import android.nearby.aidl.FastPairAccountDevicesMetadataRequestParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataParcel;
+import android.nearby.aidl.FastPairAntispoofKeyDeviceMetadataRequestParcel;
+import android.nearby.aidl.FastPairDeviceMetadataParcel;
+import android.nearby.aidl.FastPairEligibleAccountParcel;
+import android.nearby.aidl.FastPairEligibleAccountsRequestParcel;
+import android.nearby.aidl.FastPairManageAccountDeviceRequestParcel;
+import android.nearby.aidl.FastPairManageAccountRequestParcel;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.nearby.fastpair.footprint.FastPairUploadInfo;
+
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import service.proto.Cache;
+import service.proto.FastPairString;
+import service.proto.Rpcs;
+
+public class FastPairDataProviderTest {
+
+    private static final Account ACCOUNT = new Account("abc@google.com", "type1");
+    private static final byte[] MODEL_ID = new byte[]{7, 9};
+    private static final int BLE_TX_POWER = 5;
+    private static final String CONNECT_SUCCESS_COMPANION_APP_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_INSTALLED";
+    private static final String CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED =
+            "CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED";
+    private static final int DEVICE_TYPE = 1;
+    private static final String DOWNLOAD_COMPANION_APP_DESCRIPTION =
+            "DOWNLOAD_COMPANION_APP_DESCRIPTION";
+    private static final String FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION =
+            "FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION";
+    private static final byte[] IMAGE = new byte[]{7, 9};
+    private static final String IMAGE_URL = "IMAGE_URL";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION =
+            "INITIAL_NOTIFICATION_DESCRIPTION";
+    private static final String INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT =
+            "INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT";
+    private static final String INITIAL_PAIRING_DESCRIPTION = "INITIAL_PAIRING_DESCRIPTION";
+    private static final String INTENT_URI = "INTENT_URI";
+    private static final String OPEN_COMPANION_APP_DESCRIPTION = "OPEN_COMPANION_APP_DESCRIPTION";
+    private static final String RETRO_ACTIVE_PAIRING_DESCRIPTION =
+            "RETRO_ACTIVE_PAIRING_DESCRIPTION";
+    private static final String SUBSEQUENT_PAIRING_DESCRIPTION = "SUBSEQUENT_PAIRING_DESCRIPTION";
+    private static final float TRIGGER_DISTANCE = 111;
+    private static final String TRUE_WIRELESS_IMAGE_URL_CASE = "TRUE_WIRELESS_IMAGE_URL_CASE";
+    private static final String TRUE_WIRELESS_IMAGE_URL_LEFT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_LEFT_BUD";
+    private static final String TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD =
+            "TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD";
+    private static final String UNABLE_TO_CONNECT_DESCRIPTION = "UNABLE_TO_CONNECT_DESCRIPTION";
+    private static final String UNABLE_TO_CONNECT_TITLE = "UNABLE_TO_CONNECT_TITLE";
+    private static final String UPDATE_COMPANION_APP_DESCRIPTION =
+            "UPDATE_COMPANION_APP_DESCRIPTION";
+    private static final String WAIT_LAUNCH_COMPANION_APP_DESCRIPTION =
+            "WAIT_LAUNCH_COMPANION_APP_DESCRIPTION";
+    private static final byte[] ACCOUNT_KEY = new byte[]{3};
+    private static final byte[] SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS = new byte[]{2, 8};
+    private static final byte[] ANTI_SPOOFING_KEY = new byte[]{4, 5, 6};
+    private static final String ACTION_URL = "ACTION_URL";
+    private static final String APP_NAME = "APP_NAME";
+    private static final byte[] AUTHENTICATION_PUBLIC_KEY_SEC_P256R1 = new byte[]{5, 7};
+    private static final String DESCRIPTION = "DESCRIPTION";
+    private static final String DEVICE_NAME = "DEVICE_NAME";
+    private static final String DISPLAY_URL = "DISPLAY_URL";
+    private static final long FIRST_OBSERVATION_TIMESTAMP_MILLIS = 8393L;
+    private static final String ICON_FIFE_URL = "ICON_FIFE_URL";
+    private static final byte[] ICON_PNG = new byte[]{2, 5};
+    private static final String ID = "ID";
+    private static final long LAST_OBSERVATION_TIMESTAMP_MILLIS = 934234L;
+    private static final String MAC_ADDRESS = "MAC_ADDRESS";
+    private static final String NAME = "NAME";
+    private static final String PACKAGE_NAME = "PACKAGE_NAME";
+    private static final long PENDING_APP_INSTALL_TIMESTAMP_MILLIS = 832393L;
+    private static final int RSSI = 9;
+    private static final String TITLE = "TITLE";
+    private static final String TRIGGER_ID = "TRIGGER_ID";
+    private static final int TX_POWER = 63;
+
+    @Mock ProxyFastPairDataProvider mProxyFastPairDataProvider;
+
+    FastPairDataProvider mFastPairDataProvider;
+    FastPairEligibleAccountParcel[] mFastPairEligibleAccountParcels =
+            { genHappyPathFastPairEligibleAccountParcel() };
+    FastPairAntispoofKeyDeviceMetadataParcel mFastPairAntispoofKeyDeviceMetadataParcel =
+            genHappyPathFastPairAntispoofKeyDeviceMetadataParcel();
+    FastPairUploadInfo mFastPairUploadInfo = genHappyPathFastPairUploadInfo();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mFastPairDataProvider = FastPairDataProvider.init(context);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testFailurePath_throwsException() throws IllegalStateException {
+        mFastPairDataProvider = FastPairDataProvider.getInstance();
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.loadFastPairEligibleAccounts(); });
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.loadFastPairAntispoofKeyDeviceMetadata(MODEL_ID); });
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.loadFastPairDeviceWithAccountKey(ACCOUNT); });
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.loadFastPairDeviceWithAccountKey(
+                            ACCOUNT, ImmutableList.of()); });
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.optIn(ACCOUNT); });
+        assertThrows(
+                IllegalStateException.class,
+                () -> {
+                    mFastPairDataProvider.upload(ACCOUNT, mFastPairUploadInfo); });
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testLoadFastPairAntispoofKeyDeviceMetadata_receivesResponse()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        when(mProxyFastPairDataProvider.loadFastPairAntispoofKeyDeviceMetadata(any()))
+                .thenReturn(mFastPairAntispoofKeyDeviceMetadataParcel);
+
+        mFastPairDataProvider.loadFastPairAntispoofKeyDeviceMetadata(MODEL_ID);
+        ArgumentCaptor<FastPairAntispoofKeyDeviceMetadataRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairAntispoofKeyDeviceMetadataRequestParcel.class);
+        verify(mProxyFastPairDataProvider).loadFastPairAntispoofKeyDeviceMetadata(captor.capture());
+        assertThat(captor.getValue().modelId).isSameInstanceAs(MODEL_ID);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testOptIn_finishesSuccessfully()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        doNothing().when(mProxyFastPairDataProvider).manageFastPairAccount(any());
+        mFastPairDataProvider.optIn(ACCOUNT);
+        ArgumentCaptor<FastPairManageAccountRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairManageAccountRequestParcel.class);
+        verify(mProxyFastPairDataProvider).manageFastPairAccount(captor.capture());
+        assertThat(captor.getValue().account).isSameInstanceAs(ACCOUNT);
+        assertThat(captor.getValue().requestType).isEqualTo(
+                FastPairDataProviderService.MANAGE_REQUEST_ADD);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testUpload_finishesSuccessfully()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        doNothing().when(mProxyFastPairDataProvider).manageFastPairAccountDevice(any());
+        mFastPairDataProvider.upload(ACCOUNT, mFastPairUploadInfo);
+        ArgumentCaptor<FastPairManageAccountDeviceRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairManageAccountDeviceRequestParcel.class);
+        verify(mProxyFastPairDataProvider).manageFastPairAccountDevice(captor.capture());
+        assertThat(captor.getValue().account).isSameInstanceAs(ACCOUNT);
+        assertThat(captor.getValue().requestType).isEqualTo(
+                FastPairDataProviderService.MANAGE_REQUEST_ADD);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testLoadFastPairEligibleAccounts_receivesOneAccount()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        when(mProxyFastPairDataProvider.loadFastPairEligibleAccounts(any()))
+                .thenReturn(mFastPairEligibleAccountParcels);
+        assertThat(mFastPairDataProvider.loadFastPairEligibleAccounts().size())
+                .isEqualTo(1);
+        ArgumentCaptor<FastPairEligibleAccountsRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairEligibleAccountsRequestParcel.class);
+        verify(mProxyFastPairDataProvider).loadFastPairEligibleAccounts(captor.capture());
+        assertThat(captor.getValue()).isNotNull();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testLoadFastPairDeviceWithAccountKey_finishesSuccessfully()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        when(mProxyFastPairDataProvider.loadFastPairAccountDevicesMetadata(any()))
+                .thenReturn(null);
+
+        mFastPairDataProvider.loadFastPairDeviceWithAccountKey(ACCOUNT);
+        ArgumentCaptor<FastPairAccountDevicesMetadataRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairAccountDevicesMetadataRequestParcel.class);
+        verify(mProxyFastPairDataProvider).loadFastPairAccountDevicesMetadata(captor.capture());
+        assertThat(captor.getValue().account).isSameInstanceAs(ACCOUNT);
+        assertThat(captor.getValue().deviceAccountKeys).isEmpty();
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testLoadFastPairDeviceWithAccountKeyDeviceAccountKeys_finishesSuccessfully()  {
+        mFastPairDataProvider.setProxyDataProvider(mProxyFastPairDataProvider);
+        when(mProxyFastPairDataProvider.loadFastPairAccountDevicesMetadata(any()))
+                .thenReturn(null);
+
+        mFastPairDataProvider.loadFastPairDeviceWithAccountKey(
+                ACCOUNT, ImmutableList.of(ACCOUNT_KEY));
+        ArgumentCaptor<FastPairAccountDevicesMetadataRequestParcel> captor =
+                ArgumentCaptor.forClass(FastPairAccountDevicesMetadataRequestParcel.class);
+        verify(mProxyFastPairDataProvider).loadFastPairAccountDevicesMetadata(captor.capture());
+        assertThat(captor.getValue().account).isSameInstanceAs(ACCOUNT);
+        assertThat(captor.getValue().deviceAccountKeys.length).isEqualTo(1);
+        assertThat(captor.getValue().deviceAccountKeys[0].byteArray).isSameInstanceAs(ACCOUNT_KEY);
+    }
+
+    private static FastPairEligibleAccountParcel genHappyPathFastPairEligibleAccountParcel() {
+        FastPairEligibleAccountParcel parcel = new FastPairEligibleAccountParcel();
+        parcel.account = ACCOUNT;
+        parcel.optIn = true;
+
+        return parcel;
+    }
+
+    private static FastPairAntispoofKeyDeviceMetadataParcel
+                genHappyPathFastPairAntispoofKeyDeviceMetadataParcel() {
+        FastPairAntispoofKeyDeviceMetadataParcel parcel =
+                new FastPairAntispoofKeyDeviceMetadataParcel();
+        parcel.antispoofPublicKey = ANTI_SPOOFING_KEY;
+        parcel.deviceMetadata = genHappyPathFastPairDeviceMetadataParcel();
+
+        return parcel;
+    }
+
+    private static FastPairDeviceMetadataParcel genHappyPathFastPairDeviceMetadataParcel() {
+        FastPairDeviceMetadataParcel parcel = new FastPairDeviceMetadataParcel();
+
+        parcel.bleTxPower = BLE_TX_POWER;
+        parcel.connectSuccessCompanionAppInstalled = CONNECT_SUCCESS_COMPANION_APP_INSTALLED;
+        parcel.connectSuccessCompanionAppNotInstalled =
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED;
+        parcel.deviceType = DEVICE_TYPE;
+        parcel.downloadCompanionAppDescription = DOWNLOAD_COMPANION_APP_DESCRIPTION;
+        parcel.failConnectGoToSettingsDescription = FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION;
+        parcel.image = IMAGE;
+        parcel.imageUrl = IMAGE_URL;
+        parcel.initialNotificationDescription = INITIAL_NOTIFICATION_DESCRIPTION;
+        parcel.initialNotificationDescriptionNoAccount =
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT;
+        parcel.initialPairingDescription = INITIAL_PAIRING_DESCRIPTION;
+        parcel.intentUri = INTENT_URI;
+        parcel.name = NAME;
+        parcel.openCompanionAppDescription = OPEN_COMPANION_APP_DESCRIPTION;
+        parcel.retroactivePairingDescription = RETRO_ACTIVE_PAIRING_DESCRIPTION;
+        parcel.subsequentPairingDescription = SUBSEQUENT_PAIRING_DESCRIPTION;
+        parcel.triggerDistance = TRIGGER_DISTANCE;
+        parcel.trueWirelessImageUrlCase = TRUE_WIRELESS_IMAGE_URL_CASE;
+        parcel.trueWirelessImageUrlLeftBud = TRUE_WIRELESS_IMAGE_URL_LEFT_BUD;
+        parcel.trueWirelessImageUrlRightBud = TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD;
+        parcel.unableToConnectDescription = UNABLE_TO_CONNECT_DESCRIPTION;
+        parcel.unableToConnectTitle = UNABLE_TO_CONNECT_TITLE;
+        parcel.updateCompanionAppDescription = UPDATE_COMPANION_APP_DESCRIPTION;
+        parcel.waitLaunchCompanionAppDescription = WAIT_LAUNCH_COMPANION_APP_DESCRIPTION;
+
+        return parcel;
+    }
+
+    private static Cache.StoredDiscoveryItem genHappyPathStoredDiscoveryItem() {
+        Cache.StoredDiscoveryItem.Builder storedDiscoveryItemBuilder =
+                Cache.StoredDiscoveryItem.newBuilder();
+        storedDiscoveryItemBuilder.setActionUrl(ACTION_URL);
+        storedDiscoveryItemBuilder.setActionUrlType(Cache.ResolvedUrlType.WEBPAGE);
+        storedDiscoveryItemBuilder.setAppName(APP_NAME);
+        storedDiscoveryItemBuilder.setAuthenticationPublicKeySecp256R1(
+                ByteString.copyFrom(AUTHENTICATION_PUBLIC_KEY_SEC_P256R1));
+        storedDiscoveryItemBuilder.setDescription(DESCRIPTION);
+        storedDiscoveryItemBuilder.setDeviceName(DEVICE_NAME);
+        storedDiscoveryItemBuilder.setDisplayUrl(DISPLAY_URL);
+        storedDiscoveryItemBuilder.setFirstObservationTimestampMillis(
+                FIRST_OBSERVATION_TIMESTAMP_MILLIS);
+        storedDiscoveryItemBuilder.setIconFifeUrl(ICON_FIFE_URL);
+        storedDiscoveryItemBuilder.setIconPng(ByteString.copyFrom(ICON_PNG));
+        storedDiscoveryItemBuilder.setId(ID);
+        storedDiscoveryItemBuilder.setLastObservationTimestampMillis(
+                LAST_OBSERVATION_TIMESTAMP_MILLIS);
+        storedDiscoveryItemBuilder.setMacAddress(MAC_ADDRESS);
+        storedDiscoveryItemBuilder.setPackageName(PACKAGE_NAME);
+        storedDiscoveryItemBuilder.setPendingAppInstallTimestampMillis(
+                PENDING_APP_INSTALL_TIMESTAMP_MILLIS);
+        storedDiscoveryItemBuilder.setRssi(RSSI);
+        storedDiscoveryItemBuilder.setState(Cache.StoredDiscoveryItem.State.STATE_ENABLED);
+        storedDiscoveryItemBuilder.setTitle(TITLE);
+        storedDiscoveryItemBuilder.setTriggerId(TRIGGER_ID);
+        storedDiscoveryItemBuilder.setTxPower(TX_POWER);
+
+        FastPairString.FastPairStrings.Builder stringsBuilder =
+                FastPairString.FastPairStrings.newBuilder();
+        stringsBuilder.setPairingFinishedCompanionAppInstalled(
+                CONNECT_SUCCESS_COMPANION_APP_INSTALLED);
+        stringsBuilder.setPairingFinishedCompanionAppNotInstalled(
+                CONNECT_SUCCESS_COMPANION_APP_NOT_INSTALLED);
+        stringsBuilder.setPairingFailDescription(
+                FAIL_CONNECT_GOTO_SETTINGS_DESCRIPTION);
+        stringsBuilder.setTapToPairWithAccount(
+                INITIAL_NOTIFICATION_DESCRIPTION);
+        stringsBuilder.setTapToPairWithoutAccount(
+                INITIAL_NOTIFICATION_DESCRIPTION_NO_ACCOUNT);
+        stringsBuilder.setInitialPairingDescription(INITIAL_PAIRING_DESCRIPTION);
+        stringsBuilder.setRetroactivePairingDescription(RETRO_ACTIVE_PAIRING_DESCRIPTION);
+        stringsBuilder.setSubsequentPairingDescription(SUBSEQUENT_PAIRING_DESCRIPTION);
+        stringsBuilder.setWaitAppLaunchDescription(WAIT_LAUNCH_COMPANION_APP_DESCRIPTION);
+        storedDiscoveryItemBuilder.setFastPairStrings(stringsBuilder.build());
+
+        Cache.FastPairInformation.Builder fpInformationBuilder =
+                Cache.FastPairInformation.newBuilder();
+        Rpcs.TrueWirelessHeadsetImages.Builder imagesBuilder =
+                Rpcs.TrueWirelessHeadsetImages.newBuilder();
+        imagesBuilder.setCaseUrl(TRUE_WIRELESS_IMAGE_URL_CASE);
+        imagesBuilder.setLeftBudUrl(TRUE_WIRELESS_IMAGE_URL_LEFT_BUD);
+        imagesBuilder.setRightBudUrl(TRUE_WIRELESS_IMAGE_URL_RIGHT_BUD);
+        fpInformationBuilder.setTrueWirelessImages(imagesBuilder.build());
+        fpInformationBuilder.setDeviceType(Rpcs.DeviceType.HEADPHONES);
+
+        storedDiscoveryItemBuilder.setFastPairInformation(fpInformationBuilder.build());
+        storedDiscoveryItemBuilder.setTxPower(TX_POWER);
+
+        storedDiscoveryItemBuilder.setIconPng(ByteString.copyFrom(ICON_PNG));
+        storedDiscoveryItemBuilder.setIconFifeUrl(ICON_FIFE_URL);
+        storedDiscoveryItemBuilder.setActionUrl(ACTION_URL);
+
+        return storedDiscoveryItemBuilder.build();
+    }
+
+    private static FastPairUploadInfo genHappyPathFastPairUploadInfo() {
+        return new FastPairUploadInfo(
+                genHappyPathStoredDiscoveryItem(),
+                ByteString.copyFrom(ACCOUNT_KEY),
+                ByteString.copyFrom(SHA256_ACCOUNT_KEY_PUBLIC_ADDRESS));
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
index eeea319..35f87f1 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/provider/UtilsTest.java
@@ -110,8 +110,6 @@
     private static final byte[] ICON_PNG = new byte[]{2, 5};
     private static final String ID = "ID";
     private static final long LAST_OBSERVATION_TIMESTAMP_MILLIS = 934234L;
-    private static final int LAST_USER_EXPERIENCE = 1;
-    private static final long LOST_MILLIS = 393284L;
     private static final String MAC_ADDRESS = "MAC_ADDRESS";
     private static final String NAME = "NAME";
     private static final String PACKAGE_NAME = "PACKAGE_NAME";
@@ -121,7 +119,6 @@
     private static final String TITLE = "TITLE";
     private static final String TRIGGER_ID = "TRIGGER_ID";
     private static final int TX_POWER = 63;
-    private static final int TYPE = 1;
 
     @Test
     @SdkSuppress(minSdkVersion = 32, codeName = "T")
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java
new file mode 100644
index 0000000..0fe28df
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/ArrayUtilsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Test;
+
+public final class ArrayUtilsTest {
+
+    private static final byte[] BYTES_ONE = new byte[] {7, 9};
+    private static final byte[] BYTES_TWO = new byte[] {8};
+    private static final byte[] BYTES_EMPTY = new byte[] {};
+    private static final byte[] BYTES_ALL = new byte[] {7, 9, 8};
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConcatByteArraysNoInput() {
+        assertThat(ArrayUtils.concatByteArrays().length).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConcatByteArraysOneEmptyArray() {
+        assertThat(ArrayUtils.concatByteArrays(BYTES_EMPTY).length).isEqualTo(0);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConcatByteArraysOneNonEmptyArray() {
+        assertThat(ArrayUtils.concatByteArrays(BYTES_ONE)).isEqualTo(BYTES_ONE);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConcatByteArraysMultipleNonEmptyArrays() {
+        assertThat(ArrayUtils.concatByteArrays(BYTES_ONE, BYTES_TWO)).isEqualTo(BYTES_ALL);
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 32, codeName = "T")
+    public void testConcatByteArraysMultipleArrays() {
+        assertThat(ArrayUtils.concatByteArrays(BYTES_ONE, BYTES_EMPTY, BYTES_TWO))
+                .isEqualTo(BYTES_ALL);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java
index 1a22412..71ade2a 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/BroadcastPermissionsTest.java
@@ -93,4 +93,9 @@
         assertThat(BroadcastPermissions.getPermissionLevel(mMockContext, UID, PID))
                 .isEqualTo(PERMISSION_BLUETOOTH_ADVERTISE);
     }
+
+    @Test
+    public void test_enforceBroadcastPermission() {
+        BroadcastPermissions.enforceBroadcastPermission(mMockContext, mCallerIdentity);
+    }
 }
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/DataUtilsTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/DataUtilsTest.java
index f098600..a742b3d 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/util/DataUtilsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/DataUtilsTest.java
@@ -57,10 +57,6 @@
     private static final String MESSAGE_RETROACTIVE_PAIR_DESCRIPTION = "message 7";
     private static final String MESSAGE_WAIT_LAUNCH_COMPANION_APP_DESCRIPTION = "message 8";
     private static final String MESSAGE_FAIL_CONNECT_DESCRIPTION = "message 9";
-    private static final String MESSAGE_FAST_PAIR_TV_CONNECT_DEVICE_NO_ACCOUNT_DESCRIPTION =
-            "message 10";
-    private static final String MESSAGE_ASSISTANT_HALF_SHEET_DESCRIPTION = "message 11";
-    private static final String MESSAGE_ASSISTANT_NOTIFICATION_DESCRIPTION = "message 12";
 
     @Test
     public void test_toScanFastPairStoreItem_withAccount() {
@@ -107,6 +103,24 @@
     public void test_toString() {
         Cache.ScanFastPairStoreItem item = DataUtils.toScanFastPairStoreItem(
                 createObservedDeviceResponse(), BLUETOOTH_ADDRESS, ACCOUNT);
+
+        assertThat(DataUtils.toString(item))
+                .isEqualTo("ScanFastPairStoreItem=[address:00:11:22:33:FF:EE, "
+                        + "actionUrl:intent:#Intent;action=cto_be_set%3AACTION_MAGIC_PAIR;"
+                        + "package=to_be_set;component=to_be_set;"
+                        + "to_be_set%3AEXTRA_COMPANION_APP=test_package;"
+                        + "end, deviceName:My device, "
+                        + "iconFifeUrl:device_image_url, "
+                        + "fastPairStrings:FastPairStrings[tapToPairWithAccount=message 1, "
+                        + "tapToPairWithoutAccount=message 2, "
+                        + "initialPairingDescription=message 3 My device, "
+                        + "pairingFinishedCompanionAppInstalled=message 4, "
+                        + "pairingFinishedCompanionAppNotInstalled=message 5, "
+                        + "subsequentPairingDescription=message 6, "
+                        + "retroactivePairingDescription=message 7, "
+                        + "waitAppLaunchDescription=message 8, "
+                        + "pairingFailDescription=message 9]]");
+
         FastPairStrings strings = item.getFastPairStrings();
 
         assertThat(DataUtils.toString(strings))
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/EnvironmentTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/EnvironmentTest.java
new file mode 100644
index 0000000..e167cf4
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/EnvironmentTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util;
+
+import org.junit.Test;
+
+public class EnvironmentTest {
+
+    @Test
+    public void getNearbyDirectory() {
+        Environment.getNearbyDirectory();
+        Environment.getNearbyDirectory(1);
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java
new file mode 100644
index 0000000..f0294fc
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpIdentityV1Test.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class CryptorImpIdentityV1Test {
+    private static final String TAG = "CryptorImpIdentityV1Test";
+    private static final byte[] SALT = new byte[] {102, 22};
+    private static final byte[] DATA =
+            new byte[] {107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
+    private static final byte[] AUTHENTICITY_KEY =
+            new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
+
+    @Test
+    public void test_encrypt_decrypt() {
+        Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
+        byte[] encryptedData = identityCryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
+
+        assertThat(identityCryptor.decrypt(encryptedData, SALT, AUTHENTICITY_KEY)).isEqualTo(DATA);
+    }
+
+    @Test
+    public void test_encryption() {
+        Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
+        byte[] encryptedData = identityCryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
+
+        // for debugging
+        Log.d(TAG, "encrypted data is: " + Arrays.toString(encryptedData));
+
+        assertThat(encryptedData).isEqualTo(getEncryptedData());
+    }
+
+    @Test
+    public void test_decryption() {
+        Cryptor identityCryptor = CryptorImpIdentityV1.getInstance();
+        byte[] decryptedData =
+                identityCryptor.decrypt(getEncryptedData(), SALT, AUTHENTICITY_KEY);
+        // for debugging
+        Log.d(TAG, "decrypted data is: " + Arrays.toString(decryptedData));
+
+        assertThat(decryptedData).isEqualTo(DATA);
+    }
+
+    @Test
+    public void generateHmacTag() {
+        CryptorImpIdentityV1 identityCryptor = CryptorImpIdentityV1.getInstance();
+        byte[] generatedTag = identityCryptor.sign(DATA);
+        byte[] expectedTag = new byte[]{50, 116, 95, -87, 63, 123, -79, -43};
+        assertThat(generatedTag).isEqualTo(expectedTag);
+    }
+
+    private static byte[] getEncryptedData() {
+        return new byte[]{6, -31, -32, -123, 43, -92, -47, -110, -65, 126, -15, -51, -19, -43};
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java
new file mode 100644
index 0000000..3ca2575
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorImpV1Test.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Unit test for {@link CryptorImpV1}
+ */
+public final class CryptorImpV1Test {
+    private static final String TAG = "CryptorImpV1Test";
+    private static final byte[] SALT = new byte[] {102, 22};
+    private static final byte[] DATA =
+            new byte[] {107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
+    private static final byte[] AUTHENTICITY_KEY =
+            new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
+
+    @Test
+    public void test_encryption() {
+        Cryptor v1Cryptor = CryptorImpV1.getInstance();
+        byte[] encryptedData = v1Cryptor.encrypt(DATA, SALT, AUTHENTICITY_KEY);
+
+        // for debugging
+        Log.d(TAG, "encrypted data is: " + Arrays.toString(encryptedData));
+
+        assertThat(encryptedData).isEqualTo(getEncryptedData());
+    }
+
+    @Test
+    public void test_encryption_invalidInput() {
+        Cryptor v1Cryptor = CryptorImpV1.getInstance();
+        assertThat(v1Cryptor.encrypt(DATA, SALT, new byte[]{1, 2, 3, 4, 6})).isNull();
+    }
+
+    @Test
+    public void test_decryption() {
+        Cryptor v1Cryptor = CryptorImpV1.getInstance();
+        byte[] decryptedData =
+                v1Cryptor.decrypt(getEncryptedData(), SALT, AUTHENTICITY_KEY);
+        // for debugging
+        Log.d(TAG, "decrypted data is: " + Arrays.toString(decryptedData));
+
+        assertThat(decryptedData).isEqualTo(DATA);
+    }
+
+    @Test
+    public void test_decryption_invalidInput() {
+        Cryptor v1Cryptor = CryptorImpV1.getInstance();
+        assertThat(v1Cryptor.decrypt(getEncryptedData(), SALT, new byte[]{1, 2, 3, 4, 6})).isNull();
+    }
+
+    @Test
+    public void generateSign() {
+        CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
+        byte[] generatedTag = v1Cryptor.sign(DATA, AUTHENTICITY_KEY);
+        byte[] expectedTag = new byte[]{
+                100, 88, -104, 80, -66, 107, -38, 95, 34, 40, -56, -23, -90, 90, -87, 12};
+        assertThat(generatedTag).isEqualTo(expectedTag);
+    }
+
+    @Test
+    public void test_verify() {
+        CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
+        byte[] expectedTag = new byte[]{
+                100, 88, -104, 80, -66, 107, -38, 95, 34, 40, -56, -23, -90, 90, -87, 12};
+
+        assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, expectedTag)).isTrue();
+        assertThat(v1Cryptor.verify(DATA, AUTHENTICITY_KEY, DATA)).isFalse();
+    }
+
+    @Test
+    public void test_generateHmacTag_sameResult() {
+        CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
+        byte[] res1 = v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY);
+        assertThat(res1)
+                .isEqualTo(v1Cryptor.generateHmacTag(DATA, AUTHENTICITY_KEY));
+    }
+
+    @Test
+    public void test_generateHmacTag_nullData() {
+        CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
+        assertThat(v1Cryptor.generateHmacTag(/* data= */ null, AUTHENTICITY_KEY)).isNull();
+    }
+
+    @Test
+    public void test_generateHmacTag_nullKey() {
+        CryptorImpV1 v1Cryptor = CryptorImpV1.getInstance();
+        assertThat(v1Cryptor.generateHmacTag(DATA, /* authenticityKey= */ null)).isNull();
+    }
+
+    private static byte[] getEncryptedData() {
+        return new byte[]{-92, 94, -99, -97, 81, -48, -7, 119, -64, -22, 45, -49, -50, 92};
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java
new file mode 100644
index 0000000..ca612e3
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/encryption/CryptorTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link Cryptor}
+ */
+public final class CryptorTest {
+
+    private static final byte[] DATA =
+            new byte[] {107, -102, 101, 107, 20, 62, 2, 73, 113, 59, 8, -14, -58, 122};
+    private static final byte[] AUTHENTICITY_KEY =
+            new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26, -73, -36, 100};
+
+    @Test
+    public void test_computeHkdf() {
+        int outputSize = 16;
+        byte[] res1 = Cryptor.computeHkdf(DATA, AUTHENTICITY_KEY, outputSize);
+        byte[] res2 = Cryptor.computeHkdf(DATA,
+                new byte[] {-89, 88, -50, -42, -99, 57, 84, -24, 121, 1, -104, -8, -26},
+                outputSize);
+
+        assertThat(res1).hasLength(outputSize);
+        assertThat(res2).hasLength(outputSize);
+        assertThat(res1).isNotEqualTo(res2);
+        assertThat(res1)
+                .isEqualTo(CryptorImpV1.computeHkdf(DATA, AUTHENTICITY_KEY, outputSize));
+    }
+
+    @Test
+    public void test_computeHkdf_invalidInput() {
+        assertThat(Cryptor.computeHkdf(DATA, AUTHENTICITY_KEY, /* size= */ 256000))
+                .isNull();
+        assertThat(Cryptor.computeHkdf(DATA, new byte[0], /* size= */ 255))
+                .isNull();
+    }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/util/identity/CallerIdentityTest.java b/nearby/tests/unit/src/com/android/server/nearby/util/identity/CallerIdentityTest.java
new file mode 100644
index 0000000..c29cb92
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/util/identity/CallerIdentityTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.server.nearby.util.identity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+public class CallerIdentityTest {
+    private static final int UID = 100;
+    private static final int PID = 10002;
+    private static final String PACKAGE_NAME = "package_name";
+    private static final String ATTRIBUTION_TAG = "attribution_tag";
+
+    @Test
+    public void testToString() {
+        CallerIdentity callerIdentity =
+                CallerIdentity.forTest(UID, PID, PACKAGE_NAME, ATTRIBUTION_TAG);
+        assertThat(callerIdentity.toString()).isEqualTo("100/package_name[attribution_tag]");
+        assertThat(callerIdentity.isSystemServer()).isFalse();
+    }
+
+    @Test
+    public void testHashCode() {
+        CallerIdentity callerIdentity =
+                CallerIdentity.forTest(UID, PID, PACKAGE_NAME, ATTRIBUTION_TAG);
+        CallerIdentity callerIdentity1 =
+                CallerIdentity.forTest(UID, PID, PACKAGE_NAME, ATTRIBUTION_TAG);
+        assertThat(callerIdentity.hashCode()).isEqualTo(callerIdentity1.hashCode());
+    }
+}
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 3f7ed2a..7950ff7 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -87,7 +87,16 @@
     RETURN_IF_NOT_OK(checkProgramAccessible(XT_BPF_INGRESS_PROG_PATH));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_EGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_EGRESS));
     RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_INGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_INGRESS));
-    RETURN_IF_NOT_OK(attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
+
+    // For the devices that support cgroup socket filter, the socket filter
+    // should be loaded successfully by bpfloader. So we attach the filter to
+    // cgroup if the program is pinned properly.
+    // TODO: delete the if statement once all devices should support cgroup
+    // socket filter (ie. the minimum kernel version required is 4.14).
+    if (!access(CGROUP_SOCKET_PROG_PATH, F_OK)) {
+        RETURN_IF_NOT_OK(
+                attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
+    }
     return netdutils::status::ok;
 }
 
diff --git a/service-t/Android.bp b/service-t/Android.bp
index 1b9f2ec..2e7a4f3 100644
--- a/service-t/Android.bp
+++ b/service-t/Android.bp
@@ -47,9 +47,10 @@
     ],
     libs: [
         "framework-annotations-lib",
+        "framework-configinfrastructure",
         "framework-connectivity-pre-jarjar",
         "framework-connectivity-t-pre-jarjar",
-        "framework-tethering.stubs.module_lib",
+        "framework-tethering",
         "service-connectivity-pre-jarjar",
         "service-nearby-pre-jarjar",
         "ServiceConnectivityResources",
diff --git a/service-t/src/com/android/server/IpSecService.java b/service-t/src/com/android/server/IpSecService.java
index 16b9f1e..6cee08a 100644
--- a/service-t/src/com/android/server/IpSecService.java
+++ b/service-t/src/com/android/server/IpSecService.java
@@ -859,6 +859,13 @@
                             mIkey,
                             0xffffffff,
                             mIfId);
+                    mNetd.ipSecDeleteSecurityPolicy(
+                            mUid,
+                            selAddrFamily,
+                            IpSecManager.DIRECTION_FWD,
+                            mIkey,
+                            0xffffffff,
+                            mIfId);
                 }
             } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
diff --git a/service-t/src/com/android/server/ethernet/EthernetConfigStore.java b/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
index 17abbab..156b526 100644
--- a/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
+++ b/service-t/src/com/android/server/ethernet/EthernetConfigStore.java
@@ -116,6 +116,10 @@
     }
 
     public void write(String iface, IpConfiguration config) {
+        final File directory = new File(APEX_IP_CONFIG_FILE_PATH);
+        if (!directory.exists()) {
+            directory.mkdirs();
+        }
         write(iface, config, APEX_IP_CONFIG_FILE_PATH + CONFIG_FILE);
     }
 
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 56c21eb..0605abe 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -269,10 +269,10 @@
         private final Set<Integer> mRequestIds = new ArraySet<>();
 
         private volatile @Nullable IpClientManager mIpClient;
-        private @NonNull NetworkCapabilities mCapabilities;
+        private NetworkCapabilities mCapabilities;
         private @Nullable EthernetIpClientCallback mIpClientCallback;
         private @Nullable EthernetNetworkAgent mNetworkAgent;
-        private @Nullable IpConfiguration mIpConfig;
+        private IpConfiguration mIpConfig;
 
         /**
          * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet
@@ -469,7 +469,9 @@
             // TODO: Update this logic to only do a restart if required. Although a restart may
             //  be required due to the capabilities or ipConfiguration values, not all
             //  capabilities changes require a restart.
-            restart();
+            if (mIpClient != null) {
+                restart();
+            }
         }
 
         boolean isRestricted() {
@@ -558,6 +560,13 @@
 
         void updateNeighborLostEvent(String logMsg) {
             Log.i(TAG, "updateNeighborLostEvent " + logMsg);
+            if (mIpConfig.getIpAssignment() == IpAssignment.STATIC) {
+                // Ignore NUD failures for static IP configurations, where restarting the IpClient
+                // will not fix connectivity.
+                // In this scenario, NetworkMonitor will not verify the network, so it will
+                // eventually be torn down.
+                return;
+            }
             // Reachability lost will be seen only if the gateway is not reachable.
             // Since ethernet FW doesn't have the mechanism to scan for new networks
             // like WiFi, simply restart.
@@ -586,6 +595,14 @@
         }
 
         private void stop() {
+            // Unregister NetworkAgent before stopping IpClient, so destroyNativeNetwork (which
+            // deletes routes) hopefully happens before stop() finishes execution. Otherwise, it may
+            // delete the new routes when IpClient gets restarted.
+            if (mNetworkAgent != null) {
+                mNetworkAgent.unregister();
+                mNetworkAgent = null;
+            }
+
             // Invalidate all previous start requests
             if (mIpClient != null) {
                 mIpClient.shutdown();
@@ -595,10 +612,6 @@
 
             mIpClientCallback = null;
 
-            if (mNetworkAgent != null) {
-                mNetworkAgent.unregister();
-                mNetworkAgent = null;
-            }
             mLinkProperties.clear();
         }
 
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index edf04b2..f6a55c8 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -289,7 +289,7 @@
 
         enforceAdminPermission(iface, false, "enableInterface()");
 
-        mTracker.enableInterface(iface, new EthernetCallback(cb));
+        mTracker.setInterfaceEnabled(iface, true /* enabled */, new EthernetCallback(cb));
     }
 
     @Override
@@ -301,7 +301,7 @@
 
         enforceAdminPermission(iface, false, "disableInterface()");
 
-        mTracker.disableInterface(iface, new EthernetCallback(cb));
+        mTracker.setInterfaceEnabled(iface, false /* enabled */, new EthernetCallback(cb));
     }
 
     @Override
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 00dff5b..852cf42 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -302,9 +302,14 @@
         final int state = getInterfaceState(iface);
         final int role = getInterfaceRole(iface);
         final IpConfiguration config = getIpConfigurationForCallback(iface, state);
+        final boolean isRestricted = isRestrictedInterface(iface);
         final int n = mListeners.beginBroadcast();
         for (int i = 0; i < n; i++) {
             try {
+                if (isRestricted) {
+                    final ListenerInfo info = (ListenerInfo) mListeners.getBroadcastCookie(i);
+                    if (!info.canUseRestrictedNetworks) continue;
+                }
                 mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config);
             } catch (RemoteException e) {
                 // Do nothing here.
@@ -366,15 +371,9 @@
     }
 
     @VisibleForTesting(visibility = PACKAGE)
-    protected void enableInterface(@NonNull final String iface,
+    protected void setInterfaceEnabled(@NonNull final String iface, boolean enabled,
             @Nullable final EthernetCallback cb) {
-        mHandler.post(() -> updateInterfaceState(iface, true, cb));
-    }
-
-    @VisibleForTesting(visibility = PACKAGE)
-    protected void disableInterface(@NonNull final String iface,
-            @Nullable final EthernetCallback cb) {
-        mHandler.post(() -> updateInterfaceState(iface, false, cb));
+        mHandler.post(() -> updateInterfaceState(iface, enabled, cb));
     }
 
     IpConfiguration getIpConfiguration(String iface) {
@@ -622,31 +621,32 @@
         // restarted while it was running), we need to fake a link up notification so we
         // start configuring it.
         if (NetdUtils.hasFlag(config, INetd.IF_FLAG_RUNNING)) {
-            updateInterfaceState(iface, true);
+            // no need to send an interface state change as this is not a true "state change". The
+            // callers (maybeTrackInterface() and setTetheringInterfaceMode()) already broadcast the
+            // state change.
+            mFactory.updateInterfaceLinkState(iface, true);
         }
     }
 
     private void updateInterfaceState(String iface, boolean up) {
-        // TODO: pull EthernetCallbacks out of EthernetNetworkFactory.
         updateInterfaceState(iface, up, new EthernetCallback(null /* cb */));
     }
 
-    private void updateInterfaceState(@NonNull final String iface, final boolean up,
-            @Nullable final EthernetCallback cb) {
+    // TODO(b/225315248): enable/disableInterface() should not affect link state.
+    private void updateInterfaceState(String iface, boolean up, EthernetCallback cb) {
         final int mode = getInterfaceMode(iface);
-        final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT)
-                && mFactory.updateInterfaceLinkState(iface, up);
-
-        if (factoryLinkStateUpdated) {
-            broadcastInterfaceStateChange(iface);
-            cb.onResult(iface);
-        } else {
-            // The interface may already be in the correct state. While usually this should not be
-            // an error, since updateInterfaceState is used in setInterfaceEnabled() /
-            // setInterfaceDisabled() it has to be reported as such.
-            // It is also possible that the interface disappeared or is in server mode.
+        if (mode == INTERFACE_MODE_SERVER || !mFactory.hasInterface(iface)) {
+            // The interface is in server mode or is not tracked.
             cb.onError("Failed to set link state " + (up ? "up" : "down") + " for " + iface);
+            return;
         }
+
+        if (mFactory.updateInterfaceLinkState(iface, up)) {
+            broadcastInterfaceStateChange(iface);
+        }
+        // If updateInterfaceLinkState returns false, the interface is already in the correct state.
+        // Always return success.
+        cb.onResult(iface);
     }
 
     private void maybeUpdateServerModeInterfaceState(String iface, boolean available) {
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
index 3b44d81..ceae9ba 100644
--- a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -22,14 +22,16 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.system.ErrnoException;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BpfDump;
 import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.InterfaceParams;
-import com.android.net.module.util.Struct.U32;
+import com.android.net.module.util.Struct.S32;
 
 /**
  * Monitor interface added (without removed) and right interface name and its index to bpf map.
@@ -39,7 +41,7 @@
     // This is current path but may be changed soon.
     private static final String IFACE_INDEX_NAME_MAP_PATH =
             "/sys/fs/bpf/netd_shared/map_netd_iface_index_name_map";
-    private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
+    private final IBpfMap<S32, InterfaceMapValue> mBpfMap;
     private final INetd mNetd;
     private final Handler mHandler;
     private final Dependencies mDeps;
@@ -62,10 +64,10 @@
     @VisibleForTesting
     public static class Dependencies {
         /** Create BpfMap for updating interface and index mapping. */
-        public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
+        public IBpfMap<S32, InterfaceMapValue> getInterfaceMap() {
             try {
                 return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
-                    U32.class, InterfaceMapValue.class);
+                    S32.class, InterfaceMapValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create interface map: " + e);
                 return null;
@@ -124,7 +126,7 @@
         }
 
         try {
-            mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName));
+            mBpfMap.updateEntry(new S32(iface.index), new InterfaceMapValue(ifaceName));
         } catch (ErrnoException e) {
             Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
         }
@@ -136,4 +138,37 @@
             mHandler.post(() -> addInterface(ifName));
         }
     }
+
+    /** get interface name by interface index from bpf map */
+    public String getIfNameByIndex(final int index) {
+        try {
+            final InterfaceMapValue value = mBpfMap.getValue(new S32(index));
+            if (value == null) {
+                Log.e(TAG, "No if name entry for index " + index);
+                return null;
+            }
+            return value.getInterfaceNameString();
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to get entry for index " + index + ": " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Dump BPF map
+     *
+     * @param pw print writer
+     */
+    public void dump(final IndentingPrintWriter pw) {
+        pw.println("BPF map status:");
+        pw.increaseIndent();
+        BpfDump.dumpMapStatus(mBpfMap, pw, "IfaceIndexNameMap", IFACE_INDEX_NAME_MAP_PATH);
+        pw.decreaseIndent();
+        pw.println("BPF map content:");
+        pw.increaseIndent();
+        BpfDump.dumpMap(mBpfMap, pw, "IfaceIndexNameMap",
+                (key, value) -> "ifaceIndex=" + key.val
+                        + " ifaceName=" + value.getInterfaceNameString());
+        pw.decreaseIndent();
+    }
 }
diff --git a/service-t/src/com/android/server/net/InterfaceMapValue.java b/service-t/src/com/android/server/net/InterfaceMapValue.java
index 42c0044..95da981 100644
--- a/service-t/src/com/android/server/net/InterfaceMapValue.java
+++ b/service-t/src/com/android/server/net/InterfaceMapValue.java
@@ -16,20 +16,45 @@
 package com.android.server.net;
 
 import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
+
+import java.util.Arrays;
 
 /**
  * The value of bpf interface index map which is used for NetworkStatsService.
  */
 public class InterfaceMapValue extends Struct {
+    private static final int IF_NAME_SIZE = 16;
+
     @Field(order = 0, type = Type.ByteArray, arraysize = 16)
     public final byte[] interfaceName;
 
     public InterfaceMapValue(String iface) {
-        final byte[] ifaceArray = iface.getBytes();
-        interfaceName = new byte[16];
         // All array bytes after the interface name, if any, must be 0.
-        System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length);
+        interfaceName = Arrays.copyOf(iface.getBytes(), IF_NAME_SIZE);
+    }
+
+    /**
+     * Constructor for Struct#parse. Build this struct from byte array of interface name.
+     *
+     * @param ifName Byte array of interface name, length is expected to be IF_NAME_SIZE(16).
+     *               If longer or shorter, interface name will be truncated or padded with zeros.
+     *               All array bytes after the interface name, if any, must be 0.
+     */
+    public InterfaceMapValue(final byte[] ifName) {
+        interfaceName = Arrays.copyOf(ifName, IF_NAME_SIZE);
+    }
+
+    /** Returns the length of the null-terminated string. */
+    private int strlen(byte[] str) {
+        for (int i = 0; i < str.length; ++i) {
+            if (str[i] == '\0') {
+                return i;
+            }
+        }
+        return str.length;
+    }
+
+    public String getInterfaceNameString() {
+        return new String(interfaceName, 0 /* offset */,  strlen(interfaceName));
     }
 }
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index b08879e..cf53002 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -56,7 +56,6 @@
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.system.OsConstants.ENOENT;
-import static android.system.OsConstants.R_OK;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -134,7 +133,6 @@
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
 import android.system.ErrnoException;
-import android.system.Os;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
 import android.text.TextUtils;
@@ -162,11 +160,13 @@
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.SharedLog;
 import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.U32;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.Struct.U8;
 import com.android.net.module.util.bpf.CookieTagMapKey;
 import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.server.BpfNetMaps;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -252,6 +252,8 @@
             "/sys/fs/bpf/netd_shared/map_netd_stats_map_A";
     private static final String STATS_MAP_B_PATH =
             "/sys/fs/bpf/netd_shared/map_netd_stats_map_B";
+    private static final String IFACE_STATS_MAP_PATH =
+            "/sys/fs/bpf/netd_shared/map_netd_iface_stats_map";
 
     /**
      * DeviceConfig flag used to indicate whether the files should be stored in the apex data
@@ -406,11 +408,12 @@
      * mActiveUidCounterSet to avoid accessing kernel too frequently.
      */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
-    private final IBpfMap<U32, U8> mUidCounterSetMap;
+    private final IBpfMap<S32, U8> mUidCounterSetMap;
     private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
     private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapA;
     private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapB;
     private final IBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap;
+    private final IBpfMap<S32, StatsMapValue> mIfaceStatsMap;
 
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mUidOperations = new NetworkStats(0L, 10);
@@ -427,12 +430,7 @@
     private long mLastStatsSessionPoll;
 
     private final Object mOpenSessionCallsLock = new Object();
-    /**
-     * Map from UID to number of opened sessions. This is used for rate-limt an app to open
-     * session frequently
-     */
-    @GuardedBy("mOpenSessionCallsLock")
-    private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
+
     /**
      * Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording
      * the caller of open session and it is only for debugging.
@@ -454,6 +452,9 @@
     @NonNull
     private final BpfInterfaceMapUpdater mInterfaceMapUpdater;
 
+    @Nullable
+    private final SkDestroyListener mSkDestroyListener;
+
     private static @NonNull Clock getDefaultClock() {
         return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
                 Clock.systemUTC());
@@ -587,6 +588,19 @@
         mStatsMapA = mDeps.getStatsMapA();
         mStatsMapB = mDeps.getStatsMapB();
         mAppUidStatsMap = mDeps.getAppUidStatsMap();
+        mIfaceStatsMap = mDeps.getIfaceStatsMap();
+
+        // TODO: Remove bpfNetMaps creation and always start SkDestroyListener
+        // Following code is for the experiment to verify the SkDestroyListener refactoring. Based
+        // on the experiment flag, BpfNetMaps starts C SkDestroyListener (existing code) or
+        // NetworkStatsService starts Java SkDestroyListener (new code).
+        final BpfNetMaps bpfNetMaps = mDeps.makeBpfNetMaps(mContext);
+        if (bpfNetMaps.isSkDestroyListenerRunning()) {
+            mSkDestroyListener = null;
+        } else {
+            mSkDestroyListener = mDeps.makeSkDestroyListener(mCookieTagMap, mHandler);
+            mHandler.post(mSkDestroyListener::start);
+        }
     }
 
     /**
@@ -724,10 +738,10 @@
         }
 
         /** Get counter sets map for each UID. */
-        public IBpfMap<U32, U8> getUidCounterSetMap() {
+        public IBpfMap<S32, U8> getUidCounterSetMap() {
             try {
-                return new BpfMap<U32, U8>(UID_COUNTERSET_MAP_PATH, BpfMap.BPF_F_RDWR,
-                        U32.class, U8.class);
+                return new BpfMap<S32, U8>(UID_COUNTERSET_MAP_PATH, BpfMap.BPF_F_RDWR,
+                        S32.class, U8.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open uid counter set map: " + e);
                 return null;
@@ -778,10 +792,31 @@
             }
         }
 
+        /** Gets interface stats map */
+        public IBpfMap<S32, StatsMapValue> getIfaceStatsMap() {
+            try {
+                return new BpfMap<S32, StatsMapValue>(IFACE_STATS_MAP_PATH,
+                        BpfMap.BPF_F_RDWR, S32.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                throw new IllegalStateException("Failed to open interface stats map", e);
+            }
+        }
+
         /** Gets whether the build is userdebug. */
         public boolean isDebuggable() {
             return Build.isDebuggable();
         }
+
+        /** Create a new BpfNetMaps. */
+        public BpfNetMaps makeBpfNetMaps(Context ctx) {
+            return new BpfNetMaps(ctx);
+        }
+
+        /** Create a new SkDestroyListener. */
+        public SkDestroyListener makeSkDestroyListener(
+                IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
+            return new SkDestroyListener(cookieTagMap, handler, new SharedLog(TAG));
+        }
     }
 
     /**
@@ -1293,7 +1328,7 @@
             mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
             Log.w(TAG, "problem registering for global alert: " + e);
-        } catch (RemoteException e) {
+        } catch (RemoteException | ServiceSpecificException e) {
             // ignored; service lives in system_server
         }
         invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
@@ -1321,9 +1356,6 @@
                 mOpenSessionCallsPerCaller.put(key, Integer.sum(callsPerCaller, 1));
             }
 
-            int callsPerUid = mOpenSessionCallsPerUid.get(key.uid, 0);
-            mOpenSessionCallsPerUid.put(key.uid, callsPerUid + 1);
-
             if (key.uid == android.os.Process.SYSTEM_UID) {
                 return false;
             }
@@ -1719,7 +1751,7 @@
 
         if (set == SET_DEFAULT) {
             try {
-                mUidCounterSetMap.deleteEntry(new U32(uid));
+                mUidCounterSetMap.deleteEntry(new S32(uid));
             } catch (ErrnoException e) {
                 Log.w(TAG, "UidCounterSetMap.deleteEntry(" + uid + ") failed with errno: " + e);
             }
@@ -1727,7 +1759,7 @@
         }
 
         try {
-            mUidCounterSetMap.updateEntry(new U32(uid), new U8((short) set));
+            mUidCounterSetMap.updateEntry(new S32(uid), new U8((short) set));
         } catch (ErrnoException e) {
             Log.w(TAG, "UidCounterSetMap.updateEntry(" + uid + ", " + set
                     + ") failed with errno: " + e);
@@ -2444,7 +2476,7 @@
         deleteStatsMapTagData(mStatsMapB, uid);
 
         try {
-            mUidCounterSetMap.deleteEntry(new U32(uid));
+            mUidCounterSetMap.deleteEntry(new S32(uid));
         } catch (ErrnoException e) {
             logErrorIfNotErrNoent(e, "Failed to delete tag data from uid counter set map");
         }
@@ -2474,8 +2506,7 @@
         for (int uid : uids) {
             deleteKernelTagData(uid);
         }
-        // TODO: Remove the UID's entries from mOpenSessionCallsPerUid and
-        // mOpenSessionCallsPerCaller
+        // TODO: Remove the UID's entries from mOpenSessionCallsPerCaller.
     }
 
     /**
@@ -2713,6 +2744,12 @@
             }
 
             pw.println();
+            pw.println("InterfaceMapUpdater:");
+            pw.increaseIndent();
+            mInterfaceMapUpdater.dump(pw);
+            pw.decreaseIndent();
+
+            pw.println();
             pw.println("BPF map status:");
             pw.increaseIndent();
             dumpMapStatus(pw);
@@ -2727,6 +2764,9 @@
             dumpCookieTagMapLocked(pw);
             dumpUidCounterSetMapLocked(pw);
             dumpAppUidStatsMapLocked(pw);
+            dumpStatsMapLocked(mStatsMapA, pw, "mStatsMapA");
+            dumpStatsMapLocked(mStatsMapB, pw, "mStatsMapB");
+            dumpIfaceStatsMapLocked(pw);
             pw.decreaseIndent();
         }
     }
@@ -2794,24 +2834,14 @@
         }
     }
 
-    private <K extends Struct, V extends Struct> String getMapStatus(
-            final IBpfMap<K, V> map, final String path) {
-        if (map != null) {
-            return "OK";
-        }
-        try {
-            Os.access(path, R_OK);
-            return "NULL(map is pinned to " + path + ")";
-        } catch (ErrnoException e) {
-            return "NULL(map is not pinned to " + path + ": " + Os.strerror(e.errno) + ")";
-        }
-    }
-
     private void dumpMapStatus(final IndentingPrintWriter pw) {
-        pw.println("mCookieTagMap: " + getMapStatus(mCookieTagMap, COOKIE_TAG_MAP_PATH));
-        pw.println("mUidCounterSetMap: "
-                + getMapStatus(mUidCounterSetMap, UID_COUNTERSET_MAP_PATH));
-        pw.println("mAppUidStatsMap: " + getMapStatus(mAppUidStatsMap, APP_UID_STATS_MAP_PATH));
+        BpfDump.dumpMapStatus(mCookieTagMap, pw, "mCookieTagMap", COOKIE_TAG_MAP_PATH);
+        BpfDump.dumpMapStatus(mUidCounterSetMap, pw, "mUidCounterSetMap", UID_COUNTERSET_MAP_PATH);
+        BpfDump.dumpMapStatus(mAppUidStatsMap, pw, "mAppUidStatsMap", APP_UID_STATS_MAP_PATH);
+        BpfDump.dumpMapStatus(mStatsMapA, pw, "mStatsMapA", STATS_MAP_A_PATH);
+        BpfDump.dumpMapStatus(mStatsMapB, pw, "mStatsMapB", STATS_MAP_B_PATH);
+        // mIfaceStatsMap is always not null but dump status to be consistent with other maps.
+        BpfDump.dumpMapStatus(mIfaceStatsMap, pw, "mIfaceStatsMap", IFACE_STATS_MAP_PATH);
     }
 
     @GuardedBy("mStatsLock")
@@ -2848,6 +2878,44 @@
                         + value.txPackets);
     }
 
+    @GuardedBy("mStatsLock")
+    private void dumpStatsMapLocked(final IBpfMap<StatsMapKey, StatsMapValue> statsMap,
+            final IndentingPrintWriter pw, final String mapName) {
+        if (statsMap == null) {
+            return;
+        }
+
+        BpfDump.dumpMap(statsMap, pw, mapName,
+                "ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets",
+                (key, value) -> {
+                    final String ifName = mInterfaceMapUpdater.getIfNameByIndex(key.ifaceIndex);
+                    return key.ifaceIndex + " "
+                            + (ifName != null ? ifName : "unknown") + " "
+                            + "0x" + Long.toHexString(key.tag) + " "
+                            + key.uid + " "
+                            + key.counterSet + " "
+                            + value.rxBytes + " "
+                            + value.rxPackets + " "
+                            + value.txBytes + " "
+                            + value.txPackets;
+                });
+    }
+
+    @GuardedBy("mStatsLock")
+    private void dumpIfaceStatsMapLocked(final IndentingPrintWriter pw) {
+        BpfDump.dumpMap(mIfaceStatsMap, pw, "mIfaceStatsMap",
+                "ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets",
+                (key, value) -> {
+                    final String ifName = mInterfaceMapUpdater.getIfNameByIndex(key.val);
+                    return key.val + " "
+                            + (ifName != null ? ifName : "unknown") + " "
+                            + value.rxBytes + " "
+                            + value.rxPackets + " "
+                            + value.txBytes + " "
+                            + value.txPackets;
+                });
+    }
+
     private NetworkStats readNetworkStatsSummaryDev() {
         try {
             return mStatsFactory.readNetworkStatsSummaryDev();
@@ -2882,7 +2950,7 @@
      */
     private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
             throws RemoteException {
-        final NetworkStats uidSnapshot = readNetworkStatsUidDetail(UID_ALL,  ifaces, TAG_ALL);
+        final NetworkStats uidSnapshot = readNetworkStatsUidDetail(UID_ALL, ifaces, TAG_ALL);
 
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
@@ -2897,6 +2965,7 @@
         uidSnapshot.combineAllValues(providerStats);
 
         uidSnapshot.combineAllValues(mUidOperations);
+        uidSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
 
         return uidSnapshot;
     }
diff --git a/service-t/src/com/android/server/net/SkDestroyListener.java b/service-t/src/com/android/server/net/SkDestroyListener.java
new file mode 100644
index 0000000..7b68f89
--- /dev/null
+++ b/service-t/src/com/android/server/net/SkDestroyListener.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 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.server.net;
+
+import static android.system.OsConstants.NETLINK_INET_DIAG;
+
+import android.os.Handler;
+import android.system.ErrnoException;
+
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.net.module.util.ip.NetlinkMonitor;
+import com.android.net.module.util.netlink.InetDiagMessage;
+import com.android.net.module.util.netlink.NetlinkMessage;
+import com.android.net.module.util.netlink.StructInetDiagSockId;
+
+/**
+ * Monitor socket destroy and delete entry from cookie tag bpf map.
+ */
+public class SkDestroyListener extends NetlinkMonitor {
+    private static final int SKNLGRP_INET_TCP_DESTROY = 1;
+    private static final int SKNLGRP_INET_UDP_DESTROY = 2;
+    private static final int SKNLGRP_INET6_TCP_DESTROY = 3;
+    private static final int SKNLGRP_INET6_UDP_DESTROY = 4;
+
+    // TODO: if too many sockets are closed too quickly, this can overflow the socket buffer, and
+    // some entries in mCookieTagMap will not be freed. In order to fix this it would be needed to
+    // periodically dump all sockets and remove the tag entries for sockets that have been closed.
+    // For now, set a large-enough buffer that hundreds of sockets can be closed without getting
+    // ENOBUFS and leaking mCookieTagMap entries.
+    private static final int SOCK_RCV_BUF_SIZE = 512 * 1024;
+
+    private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
+
+    SkDestroyListener(final IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap,
+            final Handler handler, final SharedLog log) {
+        super(handler, log, "SkDestroyListener", NETLINK_INET_DIAG,
+                1 << (SKNLGRP_INET_TCP_DESTROY - 1)
+                        | 1 << (SKNLGRP_INET_UDP_DESTROY - 1)
+                        | 1 << (SKNLGRP_INET6_TCP_DESTROY - 1)
+                        | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1),
+                SOCK_RCV_BUF_SIZE);
+        mCookieTagMap = cookieTagMap;
+    }
+
+    @Override
+    public void processNetlinkMessage(final NetlinkMessage nlMsg, final long whenMs) {
+        if (!(nlMsg instanceof InetDiagMessage)) {
+            mLog.e("Received non InetDiagMessage");
+            return;
+        }
+        final StructInetDiagSockId sockId = ((InetDiagMessage) nlMsg).inetDiagMsg.id;
+        try {
+            mCookieTagMap.deleteEntry(new CookieTagMapKey(sockId.cookie));
+        } catch (ErrnoException e) {
+            mLog.e("Failed to delete CookieTagMap entry for " + sockId.cookie  + ": " + e);
+        }
+    }
+}
diff --git a/service-t/src/com/android/server/net/StatsMapKey.java b/service-t/src/com/android/server/net/StatsMapKey.java
index ea8d836..44269b3 100644
--- a/service-t/src/com/android/server/net/StatsMapKey.java
+++ b/service-t/src/com/android/server/net/StatsMapKey.java
@@ -24,8 +24,8 @@
  * Key for both stats maps.
  */
 public class StatsMapKey extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long uid;
+    @Field(order = 0, type = Type.S32)
+    public final int uid;
 
     @Field(order = 1, type = Type.U32)
     public final long tag;
@@ -33,11 +33,11 @@
     @Field(order = 2, type = Type.U32)
     public final long counterSet;
 
-    @Field(order = 3, type = Type.U32)
-    public final long ifaceIndex;
+    @Field(order = 3, type = Type.S32)
+    public final int ifaceIndex;
 
-    public StatsMapKey(final long uid, final long tag, final long counterSet,
-            final long ifaceIndex) {
+    public StatsMapKey(final int uid, final long tag, final long counterSet,
+            final int ifaceIndex) {
         this.uid = uid;
         this.tag = tag;
         this.counterSet = counterSet;
diff --git a/service-t/src/com/android/server/net/UidStatsMapKey.java b/service-t/src/com/android/server/net/UidStatsMapKey.java
index 2849f94..59025fd 100644
--- a/service-t/src/com/android/server/net/UidStatsMapKey.java
+++ b/service-t/src/com/android/server/net/UidStatsMapKey.java
@@ -24,10 +24,10 @@
  * Key for uid stats map.
  */
 public class UidStatsMapKey extends Struct {
-    @Field(order = 0, type = Type.U32)
-    public final long uid;
+    @Field(order = 0, type = Type.S32)
+    public final int uid;
 
-    public UidStatsMapKey(final long uid) {
+    public UidStatsMapKey(final int uid) {
         this.uid = uid;
     }
 }
diff --git a/service/Android.bp b/service/Android.bp
index b68d389..9371b02 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -22,7 +22,6 @@
 aidl_interface {
     name: "connectivity_native_aidl_interface",
     local_include_dir: "binder",
-    vendor_available: true,
     srcs: [
         "binder/android/net/connectivity/aidl/*.aidl",
     ],
@@ -143,15 +142,26 @@
         "src/**/*.java",
         ":framework-connectivity-shared-srcs",
         ":services-connectivity-shared-srcs",
+        ":statslog-connectivity-java-gen",
     ],
     libs: [
         "framework-annotations-lib",
+        "framework-configinfrastructure",
         "framework-connectivity-pre-jarjar",
+        // The framework-connectivity-t library is only available on T+ platforms
+        // so any calls to it must be protected with a check to ensure that it is
+        // available. The linter will detect any unprotected calls through an API
+        // but not direct calls to the implementation. So, this depends on the
+        // module lib stubs directly to ensure the linter will work correctly
+        // as depending on framework-connectivity-t would cause it to be compiled
+        // against the implementation because the two libraries are in the same
+        // APEX.
         "framework-connectivity-t.stubs.module_lib",
-        "framework-tethering.stubs.module_lib",
-        "framework-wifi.stubs.module_lib",
+        "framework-tethering",
+        "framework-wifi",
         "unsupportedappusage",
         "ServiceConnectivityResources",
+        "framework-statsd",
     ],
     static_libs: [
         // Do not add libs here if they are already included
@@ -169,6 +179,7 @@
         "networkstack-client",
         "PlatformProperties",
         "service-connectivity-protos",
+        "service-connectivity-stats-protos",
         "NetworkStackApiStableShims",
     ],
     apex_available: [
@@ -192,7 +203,7 @@
     libs: [
         "framework-annotations-lib",
         "framework-connectivity-pre-jarjar",
-        "framework-wifi.stubs.module_lib",
+        "framework-wifi",
         "service-connectivity-pre-jarjar",
     ],
     visibility: [
@@ -214,13 +225,18 @@
     apex_available: [
         "com.android.tethering",
     ],
-    lint: { strict_updatability_linting: true },
+    lint: {
+        strict_updatability_linting: true,
+    },
 }
 
 java_defaults {
     name: "service-connectivity-defaults",
     sdk_version: "system_server_current",
     min_sdk_version: "30",
+    defaults: [
+        "standalone-system-server-module-optimize-defaults",
+    ],
     // This library combines system server jars that have access to different bootclasspath jars.
     // Lower SDK service jars must not depend on higher SDK jars as that would let them
     // transitively depend on the wrong bootclasspath jars. Sources also cannot be added here as
@@ -241,8 +257,8 @@
         "framework-annotations-lib",
         "framework-connectivity.impl",
         "framework-connectivity-t.impl",
-        "framework-tethering.stubs.module_lib",
-        "framework-wifi.stubs.module_lib",
+        "framework-tethering",
+        "framework-wifi",
         "libprotobuf-java-nano",
     ],
     jarjar_rules: ":connectivity-jarjar-rules",
@@ -250,11 +266,11 @@
         "com.android.tethering",
     ],
     optimize: {
-        enabled: true,
-        shrink: true,
         proguard_flags_files: ["proguard.flags"],
     },
-    lint: { strict_updatability_linting: true },
+    lint: {
+        strict_updatability_linting: true,
+    },
 }
 
 // A special library created strictly for use by the tests as they need the
@@ -275,6 +291,20 @@
     installable: true,
 }
 
+java_library_static {
+    name: "service-connectivity-stats-protos",
+    sdk_version: "system_current",
+    min_sdk_version: "30",
+    proto: {
+        type: "lite",
+    },
+    srcs: [
+        "src/com/android/metrics/stats.proto",
+    ],
+    static_libs: ["ConnectivityServiceprotos"],
+    apex_available: ["com.android.tethering"],
+}
+
 genrule {
     name: "connectivity-jarjar-rules",
     defaults: ["jarjar-rules-combine-defaults"],
@@ -307,7 +337,7 @@
     ],
     out: ["service_connectivity_jarjar_rules.txt"],
     cmd: "$(location jarjar-rules-generator) " +
-        "--jars $(location :service-connectivity-pre-jarjar{.jar}) " +
+        "$(location :service-connectivity-pre-jarjar{.jar}) " +
         "$(location :service-connectivity-tiramisu-pre-jarjar{.jar}) " +
         "--prefix android.net.connectivity " +
         "--excludes $(location jarjar-excludes.txt) " +
@@ -326,9 +356,16 @@
     ],
     out: ["service_nearby_jarjar_rules.txt"],
     cmd: "$(location jarjar-rules-generator) " +
-        "--jars $(location :service-nearby-pre-jarjar{.jar}) " +
+        "$(location :service-nearby-pre-jarjar{.jar}) " +
         "--prefix com.android.server.nearby " +
         "--excludes $(location jarjar-excludes.txt) " +
         "--output $(out)",
     visibility: ["//visibility:private"],
 }
+
+genrule {
+    name: "statslog-connectivity-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module connectivity --javaPackage com.android.server --javaClass ConnectivityStatsLog",
+    out: ["com/android/server/ConnectivityStatsLog.java"],
+}
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
index 02b2875..2260596 100644
--- a/service/ServiceConnectivityResources/Android.bp
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -21,9 +21,8 @@
 
 android_app {
     name: "ServiceConnectivityResources",
-    sdk_version: "module_30",
+    sdk_version: "module_current",
     min_sdk_version: "30",
-    target_sdk_version: "33",
     resource_dirs: [
         "res",
     ],
diff --git a/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml b/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
index c490cf8..9827f4e 100644
--- a/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
@@ -17,20 +17,20 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System connectivity resources"</string>
-    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to a Wi-Fi network"</string>
+    <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"System Connectivity Resources"</string>
+    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Sign in to Wi-Fi network"</string>
     <string name="network_available_sign_in" msgid="2622520134876355561">"Sign in to network"</string>
     <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
     <skip />
-    <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+    <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
     <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Tap for options"</string>
-    <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no Internet access"</string>
-    <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no Internet access"</string>
+    <string name="mobile_no_internet" msgid="4087718456753201450">"Mobile network has no internet access"</string>
+    <string name="other_networks_no_internet" msgid="5693932964749676542">"Network has no internet access"</string>
     <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Private DNS server cannot be accessed"</string>
     <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
     <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Tap to connect anyway"</string>
     <string name="network_switch_metered" msgid="5016937523571166319">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
-    <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+    <string name="network_switch_metered_detail" msgid="1257300152739542096">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
     <string name="network_switch_metered_toast" msgid="70691146054130335">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
   <string-array name="network_switch_type_name">
     <item msgid="3004933964374161223">"mobile data"</item>
diff --git a/service/ServiceConnectivityResources/res/values-eu/strings.xml b/service/ServiceConnectivityResources/res/values-eu/strings.xml
index 2c4e431..9b39fd3 100644
--- a/service/ServiceConnectivityResources/res/values-eu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -35,7 +35,7 @@
   <string-array name="network_switch_type_name">
     <item msgid="3004933964374161223">"datu-konexioa"</item>
     <item msgid="5624324321165953608">"Wifia"</item>
-    <item msgid="5667906231066981731">"Bluetooth-a"</item>
+    <item msgid="5667906231066981731">"Bluetootha"</item>
     <item msgid="346574747471703768">"Ethernet-a"</item>
     <item msgid="5734728378097476003">"VPNa"</item>
   </string-array>
diff --git a/service/ServiceConnectivityResources/res/values-kk/strings.xml b/service/ServiceConnectivityResources/res/values-kk/strings.xml
index 00c0f39..efe23b6 100644
--- a/service/ServiceConnectivityResources/res/values-kk/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-kk/strings.xml
@@ -33,7 +33,7 @@
     <string name="network_switch_metered_detail" msgid="1257300152739542096">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
     <string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
   <string-array name="network_switch_type_name">
-    <item msgid="3004933964374161223">"мобильдік деректер"</item>
+    <item msgid="3004933964374161223">"мобильдік интернет"</item>
     <item msgid="5624324321165953608">"Wi-Fi"</item>
     <item msgid="5667906231066981731">"Bluetooth"</item>
     <item msgid="346574747471703768">"Ethernet"</item>
diff --git a/service/ServiceConnectivityResources/res/values-nb/strings.xml b/service/ServiceConnectivityResources/res/values-nb/strings.xml
index 4439048..fff6530 100644
--- a/service/ServiceConnectivityResources/res/values-nb/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-nb/strings.xml
@@ -18,7 +18,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Ressurser for systemtilkobling"</string>
-    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Logg på Wi-Fi-nettverket"</string>
+    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Logg på Wifi-nettverket"</string>
     <string name="network_available_sign_in" msgid="2622520134876355561">"Logg på nettverk"</string>
     <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
     <skip />
diff --git a/service/ServiceConnectivityResources/res/values-ro/strings.xml b/service/ServiceConnectivityResources/res/values-ro/strings.xml
index fa5848f..bf4479a 100644
--- a/service/ServiceConnectivityResources/res/values-ro/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-ro/strings.xml
@@ -18,17 +18,17 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="connectivityResourcesAppLabel" msgid="2476261877900882974">"Resurse pentru conectivitatea sistemului"</string>
-    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Conectați-vă la rețeaua Wi-Fi"</string>
-    <string name="network_available_sign_in" msgid="2622520134876355561">"Conectați-vă la rețea"</string>
+    <string name="wifi_available_sign_in" msgid="8041178343789805553">"Conectează-te la rețeaua Wi-Fi"</string>
+    <string name="network_available_sign_in" msgid="2622520134876355561">"Conectează-te la rețea"</string>
     <!-- no translation found for network_available_sign_in_detailed (8439369644697866359) -->
     <skip />
     <string name="wifi_no_internet" msgid="1326348603404555475">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
-    <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Atingeți pentru opțiuni"</string>
+    <string name="wifi_no_internet_detailed" msgid="1746921096565304090">"Atinge pentru opțiuni"</string>
     <string name="mobile_no_internet" msgid="4087718456753201450">"Rețeaua mobilă nu are acces la internet"</string>
     <string name="other_networks_no_internet" msgid="5693932964749676542">"Rețeaua nu are acces la internet"</string>
     <string name="private_dns_broken_detailed" msgid="2677123850463207823">"Serverul DNS privat nu poate fi accesat"</string>
     <string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
-    <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Atingeți pentru a vă conecta oricum"</string>
+    <string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Atinge pentru a te conecta oricum"</string>
     <string name="network_switch_metered" msgid="5016937523571166319">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
     <string name="network_switch_metered_detail" msgid="1257300152739542096">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
     <string name="network_switch_metered_toast" msgid="70691146054130335">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index bff6953..22d9b01 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -78,6 +78,27 @@
          Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
     <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
 
+    <!-- Whether the device should actively prefer bad wifi to good cell on Android 12/13.
+
+         This setting only makes sense if the system is configured not to avoid bad wifis
+         (config_networkAvoidBadWifi=0 and Settings.Global.NETWORK_AVOID_BAD_WIFI=IGNORE
+         or PROMPT), otherwise it's not used.
+
+         On Android 12 and 13, if this is 0, when ranking a bad wifi that never validated against
+         validated mobile data, the system will prefer mobile data. It will prefer wifi if wifi
+         loses validation later. This is the default behavior up to Android 13.
+         This behavior avoids the device losing internet access when walking past a wifi network
+         with no internet access.
+
+         If this is 1, then in the same scenario, the system will prefer mobile data until the wifi
+         completes its first validation attempt (or the attempt times out), and after that it
+         will prefer the wifi even if it doesn't provide internet access, unless there is a captive
+         portal on that wifi.
+
+         On Android 14 and above, the behavior is always like 1, regardless of the value of this
+         setting. -->
+    <integer translatable="false" name="config_activelyPreferBadWifi">0</integer>
+
     <!-- Array of ConnectivityManager.TYPE_xxxx constants for networks that may only
          be controlled by systemOrSignature apps.  -->
     <integer-array translatable="false" name="config_protectedNetworks">
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 3389d63..4c85e8c 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -24,6 +24,7 @@
             <item type="integer" name="config_networkMeteredMultipathPreference"/>
             <item type="array" name="config_networkSupportedKeepaliveCount"/>
             <item type="integer" name="config_networkAvoidBadWifi"/>
+            <item type="integer" name="config_activelyPreferBadWifi"/>
             <item type="array" name="config_protectedNetworks"/>
             <item type="bool" name="config_vehicleInternalNetworkAlwaysRequested"/>
             <item type="integer" name="config_networkWakeupPacketMark"/>
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 71fa8e4..799ac5c 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -47,8 +47,8 @@
       ALOGE("%s failed, error code = %d", __func__, status.code()); \
   } while (0)
 
-static void native_init(JNIEnv* env, jclass clazz) {
-  Status status = mTc.start();
+static void native_init(JNIEnv* env, jclass clazz, jboolean startSkDestroyListener) {
+  Status status = mTc.start(startSkDestroyListener);
   CHECK_LOG(status);
   if (!isOk(status)) {
     uid_t uid = getuid();
@@ -201,7 +201,7 @@
 // clang-format off
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    {"native_init", "()V",
+    {"native_init", "(Z)V",
     (void*)native_init},
     {"native_addNaughtyApp", "(I)I",
     (void*)native_addNaughtyApp},
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index de0e20a..5cd6e5d 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -87,7 +87,8 @@
 
 // Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
 jstring com_android_server_connectivity_ClatCoordinator_generateIpv6Address(
-        JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str) {
+        JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str,
+        jint mark) {
     ScopedUtfChars iface(env, ifaceStr);
     ScopedUtfChars addr4(env, v4Str);
     ScopedUtfChars prefix64(env, prefix64Str);
@@ -111,7 +112,7 @@
     }
 
     in6_addr v6;
-    if (net::clat::generateIpv6Address(iface.c_str(), v4, nat64Prefix, &v6)) {
+    if (net::clat::generateIpv6Address(iface.c_str(), v4, nat64Prefix, &v6, mark)) {
         jniThrowExceptionFmt(env, "java/io/IOException",
                              "Unable to find global source address on %s for %s", iface.c_str(),
                              prefix64.c_str());
@@ -447,7 +448,7 @@
         {"native_selectIpv4Address", "(Ljava/lang/String;I)Ljava/lang/String;",
          (void*)com_android_server_connectivity_ClatCoordinator_selectIpv4Address},
         {"native_generateIpv6Address",
-         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;",
          (void*)com_android_server_connectivity_ClatCoordinator_generateIpv6Address},
         {"native_createTunInterface", "(Ljava/lang/String;)I",
          (void*)com_android_server_connectivity_ClatCoordinator_createTunInterface},
diff --git a/service/libconnectivity/Android.bp b/service/libconnectivity/Android.bp
new file mode 100644
index 0000000..391ceac
--- /dev/null
+++ b/service/libconnectivity/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2022 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_shared {
+    name: "libcom.android.tethering.connectivity_native",
+    srcs: [
+        "src/**/*.cpp",
+    ],
+    min_sdk_version: "30",
+    static_libs: [
+        "connectivity_native_aidl_interface-V1-ndk",
+    ],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+    ],
+
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    llndk: {
+        symbol_file: "libconnectivity_native.map.txt",
+    },
+    stubs: {
+        symbol_file: "libconnectivity_native.map.txt",
+        versions: [
+            "current",
+        ],
+    },
+    header_abi_checker: {
+        enabled: true,
+        symbol_file: "libconnectivity_native.map.txt",
+    },
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
diff --git a/service/libconnectivity/include/connectivity_native.h b/service/libconnectivity/include/connectivity_native.h
new file mode 100644
index 0000000..5a2509a
--- /dev/null
+++ b/service/libconnectivity/include/connectivity_native.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef LIBCONNECTIVITY_CONNECTIVITY_NATIVE_H_
+#define LIBCONNECTIVITY_CONNECTIVITY_NATIVE_H_
+
+#include <sys/cdefs.h>
+#include <netinet/in.h>
+
+// For branches that do not yet have __ANDROID_API_U__ defined, like module
+// release branches.
+#ifndef __ANDROID_API_U__
+#define __ANDROID_API_U__ 34
+#endif
+
+__BEGIN_DECLS
+
+/**
+ * Blocks a port from being assigned during bind(). The caller is responsible for updating
+ * /proc/sys/net/ipv4/ip_local_port_range with the port being blocked so that calls to connect()
+ * will not automatically assign one of the blocked ports.
+ * Will return success even if port was already blocked.
+ *
+ * Returns 0 on success, or a POSIX error code (see errno.h) on failure:
+ *  - EINVAL for invalid port number
+ *  - EPERM if the UID of the client doesn't have network stack permission
+ *  - Other errors as per https://man7.org/linux/man-pages/man2/bpf.2.html
+ *
+ * @param port Int corresponding to port number.
+ */
+int AConnectivityNative_blockPortForBind(in_port_t port) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Unblocks a port that has previously been blocked.
+ * Will return success even if port was already unblocked.
+ *
+ * Returns 0 on success, or a POSIX error code (see errno.h) on failure:
+ *  - EINVAL for invalid port number
+ *  - EPERM if the UID of the client doesn't have network stack permission
+ *  - Other errors as per https://man7.org/linux/man-pages/man2/bpf.2.html
+ *
+ * @param port Int corresponding to port number.
+ */
+int AConnectivityNative_unblockPortForBind(in_port_t port) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Unblocks all ports that have previously been blocked.
+ *
+ * Returns 0 on success, or a POSIX error code (see errno.h) on failure:
+ *  - EINVAL for invalid port number
+ *  - EPERM if the UID of the client doesn't have network stack permission
+ *  - Other errors as per https://man7.org/linux/man-pages/man2/bpf.2.html
+ */
+int AConnectivityNative_unblockAllPortsForBind() __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Gets the list of ports that have been blocked.
+ *
+ * Returns 0 on success, or a POSIX error code (see errno.h) on failure:
+ *  - EINVAL for invalid port number
+ *  - EPERM if the UID of the client doesn't have network stack permission
+ *  - Other errors as per https://man7.org/linux/man-pages/man2/bpf.2.html
+ *
+ * @param ports Array of ports that will be filled with the port numbers.
+ * @param count Pointer to the size of the ports array; the value will be set to the total number of
+ *              blocked ports, which may be larger than the ports array that was filled.
+ */
+int AConnectivityNative_getPortsBlockedForBind(in_port_t *ports, size_t *count)
+    __INTRODUCED_IN(__ANDROID_API_U__);
+
+__END_DECLS
+
+
+#endif
diff --git a/service/libconnectivity/libconnectivity_native.map.txt b/service/libconnectivity/libconnectivity_native.map.txt
new file mode 100644
index 0000000..19b1074
--- /dev/null
+++ b/service/libconnectivity/libconnectivity_native.map.txt
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2022 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.
+#
+LIBCONNECTIVITY_NATIVE {
+  global:
+    AConnectivityNative_blockPortForBind; # apex llndk
+    AConnectivityNative_getPortsBlockedForBind; # apex llndk
+    AConnectivityNative_unblockPortForBind; # apex llndk
+    AConnectivityNative_unblockAllPortsForBind; # apex llndk
+  local:
+    *;
+};
diff --git a/service/libconnectivity/src/connectivity_native.cpp b/service/libconnectivity/src/connectivity_native.cpp
new file mode 100644
index 0000000..9545ed1
--- /dev/null
+++ b/service/libconnectivity/src/connectivity_native.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "connectivity_native.h"
+
+#include <android/binder_manager.h>
+#include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
+
+using aidl::android::net::connectivity::aidl::IConnectivityNative;
+
+
+static std::shared_ptr<IConnectivityNative> getBinder() {
+    static ndk::SpAIBinder sBinder = ndk::SpAIBinder(reinterpret_cast<AIBinder*>(
+        AServiceManager_getService("connectivity_native")));
+    return aidl::android::net::connectivity::aidl::IConnectivityNative::fromBinder(sBinder);
+}
+
+static int getErrno(const ::ndk::ScopedAStatus& status) {
+    switch (status.getExceptionCode()) {
+        case EX_NONE:
+            return 0;
+        case EX_ILLEGAL_ARGUMENT:
+            return EINVAL;
+        case EX_SECURITY:
+            return EPERM;
+        case EX_SERVICE_SPECIFIC:
+            return status.getServiceSpecificError();
+        default:
+            return EPROTO;
+    }
+}
+
+int AConnectivityNative_blockPortForBind(in_port_t port) {
+    std::shared_ptr<IConnectivityNative> c = getBinder();
+    return getErrno(c->blockPortForBind(port));
+}
+
+int AConnectivityNative_unblockPortForBind(in_port_t port) {
+    std::shared_ptr<IConnectivityNative> c = getBinder();
+    return getErrno(c->unblockPortForBind(port));
+}
+
+int AConnectivityNative_unblockAllPortsForBind() {
+    std::shared_ptr<IConnectivityNative> c = getBinder();
+    return getErrno(c->unblockAllPortsForBind());
+}
+
+int AConnectivityNative_getPortsBlockedForBind(in_port_t *ports, size_t *count) {
+    std::shared_ptr<IConnectivityNative> c = getBinder();
+    std::vector<int32_t> actualBlockedPorts;
+    int err = getErrno(c->getPortsBlockedForBind(&actualBlockedPorts));
+    if (err) {
+        return err;
+    }
+
+    for (int i = 0; i < *count && i < actualBlockedPorts.size(); i++) {
+        ports[i] = actualBlockedPorts[i];
+    }
+    *count = actualBlockedPorts.size();
+    return 0;
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index f366363..f7871f3 100644
--- a/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service/mdns/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity.mdns;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Pair;
 
@@ -38,8 +39,6 @@
  * and the list of the subtypes in the query as a {@link Pair}. If a query is failed to build, or if
  * it can not be enqueued, then call to {@link #call()} returns {@code null}.
  */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<String>>> {
 
     private static final String TAG = "MdnsQueryCallable";
@@ -81,7 +80,10 @@
         this.transactionId = transactionId;
     }
 
+    // Incompatible return type for override of Callable#call().
+    @SuppressWarnings("nullness:override.return.invalid")
     @Override
+    @Nullable
     public Pair<Integer, List<String>> call() {
         try {
             MdnsSocketClient requestSender = weakRequestSender.get();
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java
new file mode 100644
index 0000000..dee78fd
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import android.util.Log;
+
+/**
+ * MdnsAdvertiser manages advertising services per {@link com.android.server.NsdService} requests.
+ *
+ * TODO: implement
+ */
+public class MdnsAdvertiser {
+    private static final String TAG = MdnsAdvertiser.class.getSimpleName();
+    public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsAnyRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsAnyRecord.java
new file mode 100644
index 0000000..fcfe9f7
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsAnyRecord.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import android.net.DnsResolver;
+
+import java.io.IOException;
+
+/**
+ * A mDNS "ANY" record, used in mDNS questions to query for any record type.
+ */
+public class MdnsAnyRecord extends MdnsRecord {
+
+    protected MdnsAnyRecord(String[] name, MdnsPacketReader reader) throws IOException {
+        super(name, TYPE_ANY, reader, true /* isQuestion */);
+    }
+
+    protected MdnsAnyRecord(String[] name, boolean unicast) {
+        super(name, TYPE_ANY, DnsResolver.CLASS_IN /* cls */,
+                0L /* receiptTimeMillis */, unicast /* cacheFlush */, 0L /* ttlMillis */);
+    }
+
+    @Override
+    protected void readData(MdnsPacketReader reader) throws IOException {
+        // No data to read
+    }
+
+    @Override
+    protected void writeData(MdnsPacketWriter writer) throws IOException {
+        // No data to write
+    }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
index 922037b..75c7e6c 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsConfigs.java
@@ -89,4 +89,20 @@
     public static boolean preferIpv6() {
         return false;
     }
+
+    public static boolean removeServiceAfterTtlExpires() {
+        return false;
+    }
+
+    public static boolean allowSearchOptionsToRemoveExpiredService() {
+        return false;
+    }
+
+    public static boolean allowNetworkInterfaceIndexPropagation() {
+        return true;
+    }
+
+    public static boolean allowMultipleSrvRecordsPerHost() {
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsConstants.java b/service/mdns/com/android/server/connectivity/mdns/MdnsConstants.java
index 0b2066a..396be5f 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsConstants.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsConstants.java
@@ -16,19 +16,15 @@
 
 package com.android.server.connectivity.mdns;
 
-import android.annotation.TargetApi;
-import android.os.Build;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 
 /** mDNS-related constants. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 @VisibleForTesting
 public final class MdnsConstants {
     public static final int MDNS_PORT = 5353;
@@ -48,7 +44,6 @@
     private static final String MDNS_IPV4_HOST_ADDRESS = "224.0.0.251";
     private static final String MDNS_IPV6_HOST_ADDRESS = "FF02::FB";
     private static InetAddress mdnsAddress;
-    private static Charset utf8Charset;
     private MdnsConstants() {
     }
 
@@ -79,16 +74,6 @@
     }
 
     public static Charset getUtf8Charset() {
-        synchronized (MdnsConstants.class) {
-            if (utf8Charset == null) {
-                utf8Charset = getUtf8CharsetOnKitKat();
-            }
-            return utf8Charset;
-        }
-    }
-
-    @TargetApi(Build.VERSION_CODES.KITKAT)
-    private static Charset getUtf8CharsetOnKitKat() {
-        return StandardCharsets.UTF_8;
+        return UTF_8;
     }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
index bd47414..dd8a526 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
@@ -27,12 +29,10 @@
 import java.util.Objects;
 
 /** An mDNS "AAAA" or "A" record, which holds an IPv6 or IPv4 address. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 @VisibleForTesting
 public class MdnsInetAddressRecord extends MdnsRecord {
-    private Inet6Address inet6Address;
-    private Inet4Address inet4Address;
+    @Nullable private Inet6Address inet6Address;
+    @Nullable private Inet4Address inet4Address;
 
     /**
      * Constructs the {@link MdnsRecord}
@@ -43,15 +43,42 @@
      */
     public MdnsInetAddressRecord(String[] name, int type, MdnsPacketReader reader)
             throws IOException {
-        super(name, type, reader);
+        this(name, type, reader, false);
+    }
+
+    /**
+     * Constructs the {@link MdnsRecord}
+     *
+     * @param name       the service host name
+     * @param type       the type of record (either Type 'AAAA' or Type 'A')
+     * @param reader     the reader to read the record from.
+     * @param isQuestion whether the record is in the question section
+     */
+    public MdnsInetAddressRecord(String[] name, int type, MdnsPacketReader reader,
+            boolean isQuestion)
+            throws IOException {
+        super(name, type, reader, isQuestion);
+    }
+
+    public MdnsInetAddressRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
+                    long ttlMillis, InetAddress address) {
+        super(name, address instanceof Inet4Address ? TYPE_A : TYPE_AAAA,
+                MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush, ttlMillis);
+        if (address instanceof Inet4Address) {
+            inet4Address = (Inet4Address) address;
+        } else {
+            inet6Address = (Inet6Address) address;
+        }
     }
 
     /** Returns the IPv6 address. */
+    @Nullable
     public Inet6Address getInet6Address() {
         return inet6Address;
     }
 
     /** Returns the IPv4 address. */
+    @Nullable
     public Inet4Address getInet4Address() {
         return inet4Address;
     }
@@ -113,7 +140,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
@@ -125,4 +152,4 @@
                 && Objects.equals(inet4Address, ((MdnsInetAddressRecord) other).inet4Address)
                 && Objects.equals(inet6Address, ((MdnsInetAddressRecord) other).inet6Address);
     }
-}
\ No newline at end of file
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java
new file mode 100644
index 0000000..57c3c03
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsNsecRecord.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import android.net.DnsResolver;
+
+import com.android.net.module.util.CollectionUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A mDNS "NSEC" record, used in particular for negative responses (RFC6762 6.1).
+ */
+public class MdnsNsecRecord extends MdnsRecord {
+    private String[] mNextDomain;
+    private int[] mTypes;
+
+    public MdnsNsecRecord(String[] name, MdnsPacketReader reader) throws IOException {
+        this(name, reader, false);
+    }
+
+    public MdnsNsecRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)
+            throws IOException {
+        super(name, TYPE_NSEC, reader, isQuestion);
+    }
+
+    public MdnsNsecRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, long ttlMillis,
+            String[] nextDomain, int[] types) {
+        super(name, TYPE_NSEC, DnsResolver.CLASS_IN, receiptTimeMillis, cacheFlush, ttlMillis);
+        mNextDomain = nextDomain;
+        final int[] sortedTypes = Arrays.copyOf(types, types.length);
+        Arrays.sort(sortedTypes);
+        mTypes = sortedTypes;
+    }
+
+    public String[] getNextDomain() {
+        return mNextDomain;
+    }
+
+    public int[] getTypes() {
+        return mTypes;
+    }
+
+    @Override
+    protected void readData(MdnsPacketReader reader) throws IOException {
+        mNextDomain = reader.readLabels();
+        mTypes = readTypes(reader);
+    }
+
+    private int[] readTypes(MdnsPacketReader reader) throws IOException {
+        // See RFC3845 #2.1.2
+        final ArrayList<Integer> types = new ArrayList<>();
+        int prevBlockNumber = -1;
+        while (reader.getRemaining() > 0) {
+            final int blockNumber = reader.readUInt8();
+            if (blockNumber <= prevBlockNumber) {
+                throw new IOException(
+                        "Unordered block number: " + blockNumber + " after " + prevBlockNumber);
+            }
+            prevBlockNumber = blockNumber;
+            final int bitmapLength = reader.readUInt8();
+            if (bitmapLength > 32 || bitmapLength <= 0) {
+                throw new IOException("Invalid bitmap length: " + bitmapLength);
+            }
+            final byte[] bitmap = new byte[bitmapLength];
+            reader.readBytes(bitmap);
+
+            for (int bitmapIndex = 0; bitmapIndex < bitmap.length; bitmapIndex++) {
+                final byte bitmapByte = bitmap[bitmapIndex];
+                for (int bit = 0; bit < 8; bit++) {
+                    if ((bitmapByte & (1 << (7 - bit))) != 0) {
+                        types.add(blockNumber * 256 + bitmapIndex * 8 + bit);
+                    }
+                }
+            }
+        }
+
+        return CollectionUtils.toIntArray(types);
+    }
+
+    @Override
+    protected void writeData(MdnsPacketWriter writer) throws IOException {
+        // No compression as per RFC3845 2.1.1
+        writer.writeLabelsNoCompression(mNextDomain);
+
+        // type bitmaps: RFC3845 2.1.2
+        int typesBlockStart = 0;
+        int pendingBlockNumber = -1;
+        int blockLength = 0;
+        // Loop on types (which are sorted in increasing order) to find each block and determine
+        // their length; use writeTypeBlock once the length of each block has been found.
+        for (int i = 0; i < mTypes.length; i++) {
+            final int blockNumber = mTypes[i] / 256;
+            final int typeLowOrder = mTypes[i] % 256;
+            // If the low-order 8 bits are e.g. 0x10, bit number 16 (=0x10) will be set in the
+            // bitmap; this is the first bit of byte 2 (byte 0 is 0-7, 1 is 8-15, etc.)
+            final int byteIndex = typeLowOrder / 8;
+
+            if (pendingBlockNumber >= 0 && blockNumber != pendingBlockNumber) {
+                // Just reached a new block; write the previous one
+                writeTypeBlock(writer, typesBlockStart, i - 1, blockLength);
+                typesBlockStart = i;
+                blockLength = 0;
+            }
+            blockLength = Math.max(blockLength, byteIndex + 1);
+            pendingBlockNumber = blockNumber;
+        }
+
+        if (pendingBlockNumber >= 0) {
+            writeTypeBlock(writer, typesBlockStart, mTypes.length - 1, blockLength);
+        }
+    }
+
+    private void writeTypeBlock(MdnsPacketWriter writer,
+            int typesStart, int typesEnd, int bytesInBlock) throws IOException {
+        final int blockNumber = mTypes[typesStart] / 256;
+        final byte[] bytes = new byte[bytesInBlock];
+        for (int i = typesStart; i <= typesEnd; i++) {
+            final int typeLowOrder = mTypes[i] % 256;
+            bytes[typeLowOrder / 8] |= 1 << (7 - (typeLowOrder % 8));
+        }
+        writer.writeUInt8(blockNumber);
+        writer.writeUInt8(bytesInBlock);
+        writer.writeBytes(bytes);
+    }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacket.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacket.java
new file mode 100644
index 0000000..eae084a
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacket.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class holding data that can be included in a mDNS packet.
+ */
+public class MdnsPacket {
+    public final int flags;
+    public final List<MdnsRecord> questions;
+    public final List<MdnsRecord> answers;
+    public final List<MdnsRecord> authorityRecords;
+    public final List<MdnsRecord> additionalRecords;
+
+    MdnsPacket(int flags,
+            List<MdnsRecord> questions,
+            List<MdnsRecord> answers,
+            List<MdnsRecord> authorityRecords,
+            List<MdnsRecord> additionalRecords) {
+        this.flags = flags;
+        this.questions = Collections.unmodifiableList(questions);
+        this.answers = Collections.unmodifiableList(answers);
+        this.authorityRecords = Collections.unmodifiableList(authorityRecords);
+        this.additionalRecords = Collections.unmodifiableList(additionalRecords);
+    }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
index 61c5f5a..856a2cd 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketReader.java
@@ -16,8 +16,11 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
 import android.util.SparseArray;
 
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
+
 import java.io.EOFException;
 import java.io.IOException;
 import java.net.DatagramPacket;
@@ -195,6 +198,16 @@
         return val;
     }
 
+    @Nullable
+    public TextEntry readTextEntry() throws EOFException {
+        int len = readUInt8();
+        checkRemaining(len);
+        byte[] bytes = new byte[len];
+        System.arraycopy(buf, pos, bytes, 0, bytes.length);
+        pos += len;
+        return TextEntry.fromBytes(bytes);
+    }
+
     /**
      * Reads a specific number of bytes.
      *
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
new file mode 100644
index 0000000..015dbd8
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+
+/**
+ * A class used to send several packets at given time intervals.
+ * @param <T> The type of the request providing packet repeating parameters.
+ */
+public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
+    private static final boolean DBG = MdnsAdvertiser.DBG;
+    @NonNull
+    private final MdnsReplySender mReplySender;
+    @NonNull
+    protected final Handler mHandler;
+    @Nullable
+    private final PacketRepeaterCallback<T> mCb;
+
+    /**
+     * Status callback from {@link MdnsPacketRepeater}.
+     *
+     * Callbacks are called on the {@link MdnsPacketRepeater} handler thread.
+     * @param <T> The type of the request providing packet repeating parameters.
+     */
+    public interface PacketRepeaterCallback<T extends MdnsPacketRepeater.Request> {
+        /**
+         * Called when a packet was sent.
+         */
+        default void onSent(int index, @NonNull T info) {}
+
+        /**
+         * Called when the {@link MdnsPacketRepeater} is done sending packets.
+         */
+        default void onFinished(@NonNull T info) {}
+    }
+
+    /**
+     * A request to repeat packets.
+     *
+     * All methods are called in the looper thread.
+     */
+    public interface Request {
+        /**
+         * Get a packet to send for one iteration.
+         */
+        @NonNull
+        MdnsPacket getPacket(int index);
+
+        /**
+         * Get a set of destinations for the packet for one iteration.
+         */
+        @NonNull
+        Iterable<SocketAddress> getDestinations(int index);
+
+        /**
+         * Get the delay in milliseconds until the next packet transmission.
+         */
+        long getDelayMs(int nextIndex);
+
+        /**
+         * Get the number of packets that should be sent.
+         */
+        int getNumSends();
+    }
+
+    /**
+     * Get the logging tag to use.
+     */
+    @NonNull
+    protected abstract String getTag();
+
+    private final class ProbeHandler extends Handler {
+        ProbeHandler(@NonNull Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            final int index = msg.arg1;
+            final T request = (T) msg.obj;
+
+            if (index >= request.getNumSends()) {
+                if (mCb != null) {
+                    mCb.onFinished(request);
+                }
+                return;
+            }
+
+            final MdnsPacket packet = request.getPacket(index);
+            final Iterable<SocketAddress> destinations = request.getDestinations(index);
+            if (DBG) {
+                Log.v(getTag(), "Sending packets to " + destinations + " for iteration "
+                        + index + " out of " + request.getNumSends());
+            }
+            for (SocketAddress destination : destinations) {
+                try {
+                    mReplySender.sendNow(packet, destination);
+                } catch (IOException e) {
+                    Log.e(getTag(), "Error sending packet to " + destination, e);
+                }
+            }
+
+            int nextIndex = index + 1;
+            // No need to go through the last handler loop if there's no callback to call
+            if (nextIndex < request.getNumSends() || mCb != null) {
+                // TODO: consider using AlarmManager / WakeupMessage to avoid missing sending during
+                // deep sleep; but this would affect battery life, and discovered services are
+                // likely not to be available since the device is in deep sleep anyway.
+                final long delay = request.getDelayMs(nextIndex);
+                sendMessageDelayed(obtainMessage(msg.what, nextIndex, 0, request), delay);
+                if (DBG) Log.v(getTag(), "Scheduled next packet in " + delay + "ms");
+            }
+
+            // Call onSent after scheduling the next run, to allow the callback to cancel it
+            if (mCb != null) {
+                mCb.onSent(index, request);
+            }
+        }
+    }
+
+    protected MdnsPacketRepeater(@NonNull Looper looper, @NonNull MdnsReplySender replySender,
+            @Nullable PacketRepeaterCallback<T> cb) {
+        mHandler = new ProbeHandler(looper);
+        mReplySender = replySender;
+        mCb = cb;
+    }
+
+    protected void startSending(int id, @NonNull T request, long initialDelayMs) {
+        if (DBG) {
+            Log.v(getTag(), "Starting send with id " + id + ", request "
+                    + request.getClass().getSimpleName() + ", delay " + initialDelayMs);
+        }
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(id, 0, 0, request), initialDelayMs);
+    }
+
+    /**
+     * Stop sending the packets for the specified ID
+     * @return true if probing was in progress, false if this was a no-op
+     */
+    public boolean stop(int id) {
+        if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException("stop can only be called from the looper thread");
+        }
+        // Since this is run on the looper thread, messages cannot be currently processing and are
+        // all in the handler queue; unless this method is called from a message, but the current
+        // message cannot be cancelled.
+        if (mHandler.hasMessages(id)) {
+            if (DBG) {
+                Log.v(getTag(), "Stopping send on id " + id);
+            }
+            mHandler.removeMessages(id);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
index 2fed36d..1f22fa9 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPacketWriter.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.mdns;
 
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
+
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.SocketAddress;
@@ -27,7 +29,7 @@
 public class MdnsPacketWriter {
     private static final int MDNS_POINTER_MASK = 0xC000;
     private final byte[] data;
-    private final Map<Integer, String[]> labelDictionary;
+    private final Map<Integer, String[]> labelDictionary = new HashMap<>();
     private int pos = 0;
     private int savedWritePos = -1;
 
@@ -42,7 +44,15 @@
         }
 
         data = new byte[maxSize];
-        labelDictionary = new HashMap<>();
+    }
+
+    /**
+     * Constructs a writer for a new packet.
+     *
+     * @param buffer The buffer to write to.
+     */
+    public MdnsPacketWriter(byte[] buffer) {
+        data = buffer;
     }
 
     /** Returns the current write position. */
@@ -147,6 +157,12 @@
         writeBytes(utf8);
     }
 
+    public void writeTextEntry(TextEntry textEntry) throws IOException {
+        byte[] bytes = textEntry.toBytes();
+        writeUInt8(bytes.length);
+        writeBytes(bytes);
+    }
+
     /**
      * Writes a series of labels. Uses name compression.
      *
@@ -182,12 +198,7 @@
             }
             writePointer(suffixPointer);
         } else {
-            int[] offsets = new int[labels.length];
-            for (int i = 0; i < labels.length; ++i) {
-                offsets[i] = getWritePosition();
-                writeString(labels[i]);
-            }
-            writeUInt8(0); // NUL terminator
+            int[] offsets = writeLabelsNoCompression(labels);
 
             // Add entries to the label dictionary for each suffix of the label list, including
             // the whole list itself.
@@ -199,6 +210,21 @@
         }
     }
 
+    /**
+     * Write a series a labels, without using name compression.
+     *
+     * @return The offsets where each label was written to.
+     */
+    public int[] writeLabelsNoCompression(String[] labels) throws IOException {
+        int[] offsets = new int[labels.length];
+        for (int i = 0; i < labels.length; ++i) {
+            offsets[i] = getWritePosition();
+            writeString(labels[i]);
+        }
+        writeUInt8(0); // NUL terminator
+        return offsets;
+    }
+
     /** Returns the number of bytes that can still be written. */
     public int getRemaining() {
         return data.length - pos;
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsPointerRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsPointerRecord.java
index 0166815..2c7b26b 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsPointerRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsPointerRecord.java
@@ -16,20 +16,32 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
 import java.util.Arrays;
 
 /** An mDNS "PTR" record, which holds a name (the "pointer"). */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 @VisibleForTesting
 public class MdnsPointerRecord extends MdnsRecord {
     private String[] pointer;
 
     public MdnsPointerRecord(String[] name, MdnsPacketReader reader) throws IOException {
-        super(name, TYPE_PTR, reader);
+        this(name, reader, false);
+    }
+
+    public MdnsPointerRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)
+            throws IOException {
+        super(name, TYPE_PTR, reader, isQuestion);
+    }
+
+    public MdnsPointerRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
+                    long ttlMillis, String[] pointer) {
+        super(name, TYPE_PTR, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
+                ttlMillis);
+        this.pointer = pointer;
     }
 
     /** Returns the pointer as an array of labels. */
@@ -66,7 +78,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
index 24fb09e..10b8825 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -16,6 +16,10 @@
 
 package com.android.server.connectivity.mdns;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.annotation.Nullable;
 import android.os.SystemClock;
 import android.text.TextUtils;
 
@@ -24,20 +28,23 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Objects;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Abstract base class for mDNS records. Stores the header fields and provides methods for reading
  * the record from and writing it to a packet.
  */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public abstract class MdnsRecord {
     public static final int TYPE_A = 0x0001;
     public static final int TYPE_AAAA = 0x001C;
     public static final int TYPE_PTR = 0x000C;
     public static final int TYPE_SRV = 0x0021;
     public static final int TYPE_TXT = 0x0010;
+    public static final int TYPE_NSEC = 0x002f;
+    public static final int TYPE_ANY = 0x00ff;
+
+    private static final int FLAG_CACHE_FLUSH = 0x8000;
+
+    public static final long RECEIPT_TIME_NOT_SENT = 0L;
 
     /** Status indicating that the record is current. */
     public static final int STATUS_OK = 0;
@@ -57,20 +64,52 @@
      * Constructs a new record with the given name and type.
      *
      * @param reader The reader to read the record from.
+     * @param isQuestion Whether the record was included in the questions part of the message.
      * @throws IOException If an error occurs while reading the packet.
      */
-    protected MdnsRecord(String[] name, int type, MdnsPacketReader reader) throws IOException {
+    protected MdnsRecord(String[] name, int type, MdnsPacketReader reader, boolean isQuestion)
+            throws IOException {
         this.name = name;
         this.type = type;
         cls = reader.readUInt16();
-        ttlMillis = TimeUnit.SECONDS.toMillis(reader.readUInt32());
-        int dataLength = reader.readUInt16();
-
         receiptTimeMillis = SystemClock.elapsedRealtime();
 
-        reader.setLimit(dataLength);
-        readData(reader);
-        reader.clearLimit();
+        if (isQuestion) {
+            // Questions do not have TTL or data
+            ttlMillis = 0L;
+        } else {
+            ttlMillis = SECONDS.toMillis(reader.readUInt32());
+            int dataLength = reader.readUInt16();
+
+            reader.setLimit(dataLength);
+            readData(reader);
+            reader.clearLimit();
+        }
+    }
+
+    /**
+     * Constructs a new record with the given name and type.
+     *
+     * @param reader The reader to read the record from.
+     * @throws IOException If an error occurs while reading the packet.
+     */
+    // call to readData(com.android.server.connectivity.mdns.MdnsPacketReader) not allowed on given
+    // receiver.
+    @SuppressWarnings("nullness:method.invocation.invalid")
+    protected MdnsRecord(String[] name, int type, MdnsPacketReader reader) throws IOException {
+        this(name, type, reader, false);
+    }
+
+    /**
+     * Constructs a new record with the given properties.
+     */
+    protected MdnsRecord(String[] name, int type, int cls, long receiptTimeMillis,
+            boolean cacheFlush, long ttlMillis) {
+        this.name = name;
+        this.type = type;
+        this.cls = cls | (cacheFlush ? FLAG_CACHE_FLUSH : 0);
+        this.receiptTimeMillis = receiptTimeMillis;
+        this.ttlMillis = ttlMillis;
     }
 
     /**
@@ -122,13 +161,29 @@
         return type;
     }
 
+    /** Return the record's class. */
+    public final int getRecordClass() {
+        return cls & ~FLAG_CACHE_FLUSH;
+    }
+
+    /** Return whether the cache flush flag is set. */
+    public final boolean getCacheFlush() {
+        return (cls & FLAG_CACHE_FLUSH) != 0;
+    }
+
     /**
      * Returns the record's remaining TTL.
      *
+     * If the record was not sent yet (receipt time {@link #RECEIPT_TIME_NOT_SENT}), this is the
+     * original TTL of the record.
      * @param now The current system time.
      * @return The remaning TTL, in milliseconds.
      */
     public long getRemainingTTL(final long now) {
+        if (receiptTimeMillis == RECEIPT_TIME_NOT_SENT) {
+            return ttlMillis;
+        }
+
         long age = now - receiptTimeMillis;
         if (age > ttlMillis) {
             return 0;
@@ -157,7 +212,7 @@
         writer.writeUInt16(type);
         writer.writeUInt16(cls);
 
-        writer.writeUInt32(TimeUnit.MILLISECONDS.toSeconds(getRemainingTTL(now)));
+        writer.writeUInt32(MILLISECONDS.toSeconds(getRemainingTTL(now)));
 
         int dataLengthPos = writer.getWritePosition();
         writer.writeUInt16(0); // data length
@@ -183,6 +238,9 @@
 
     /** Gets the status of the record. */
     public int getStatus(final long now) {
+        if (receiptTimeMillis == RECEIPT_TIME_NOT_SENT) {
+            return STATUS_OK;
+        }
         final long age = now - receiptTimeMillis;
         if (age > ttlMillis) {
             return STATUS_EXPIRED;
@@ -194,7 +252,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (!(other instanceof MdnsRecord)) {
             return false;
         }
@@ -231,7 +289,7 @@
         }
 
         @Override
-        public boolean equals(Object other) {
+        public boolean equals(@Nullable Object other) {
             if (this == other) {
                 return true;
             }
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
new file mode 100644
index 0000000..2acd789
--- /dev/null
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.os.Looper;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.MulticastSocket;
+import java.net.SocketAddress;
+
+/**
+ * A class that handles sending mDNS replies to a {@link MulticastSocket}, possibly queueing them
+ * to be sent after some delay.
+ *
+ * TODO: implement sending after a delay, combining queued replies and duplicate answer suppression
+ */
+public class MdnsReplySender {
+    @NonNull
+    private final MulticastSocket mSocket;
+    @NonNull
+    private final Looper mLooper;
+    @NonNull
+    private final byte[] mPacketCreationBuffer;
+
+    public MdnsReplySender(@NonNull Looper looper,
+            @NonNull MulticastSocket socket, @NonNull byte[] packetCreationBuffer) {
+        mLooper = looper;
+        mSocket = socket;
+        mPacketCreationBuffer = packetCreationBuffer;
+    }
+
+    /**
+     * Send a packet immediately.
+     *
+     * Must be called on the looper thread used by the {@link MdnsReplySender}.
+     */
+    public void sendNow(@NonNull MdnsPacket packet, @NonNull SocketAddress destination)
+            throws IOException {
+        if (Thread.currentThread() != mLooper.getThread()) {
+            throw new IllegalStateException("sendNow must be called in the handler thread");
+        }
+
+        // TODO: support packets over size (send in multiple packets with TC bit set)
+        final MdnsPacketWriter writer = new MdnsPacketWriter(mPacketCreationBuffer);
+
+        writer.writeUInt16(0); // Transaction ID (advertisement: 0)
+        writer.writeUInt16(packet.flags); // Response, authoritative (rfc6762 18.4)
+        writer.writeUInt16(packet.questions.size()); // questions count
+        writer.writeUInt16(packet.answers.size()); // answers count
+        writer.writeUInt16(packet.authorityRecords.size()); // authority entries count
+        writer.writeUInt16(packet.additionalRecords.size()); // additional records count
+
+        for (MdnsRecord record : packet.questions) {
+            record.write(writer, 0L);
+        }
+        for (MdnsRecord record : packet.answers) {
+            record.write(writer, 0L);
+        }
+        for (MdnsRecord record : packet.authorityRecords) {
+            record.write(writer, 0L);
+        }
+        for (MdnsRecord record : packet.additionalRecords) {
+            record.write(writer, 0L);
+        }
+
+        final int len = writer.getWritePosition();
+        final byte[] outBuffer = new byte[len];
+        System.arraycopy(mPacketCreationBuffer, 0, outBuffer, 0, len);
+
+        mSocket.send(new DatagramPacket(outBuffer, 0, len, destination));
+    }
+}
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponse.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponse.java
index 9f3894f..623168c 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
@@ -25,8 +27,6 @@
 import java.util.List;
 
 /** An mDNS response. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class MdnsResponse {
     private final List<MdnsRecord> records;
     private final List<MdnsPointerRecord> pointerRecords;
@@ -35,6 +35,7 @@
     private MdnsInetAddressRecord inet4AddressRecord;
     private MdnsInetAddressRecord inet6AddressRecord;
     private long lastUpdateTime;
+    private int interfaceIndex = MdnsSocket.INTERFACE_INDEX_UNSPECIFIED;
 
     /** Constructs a new, empty response. */
     public MdnsResponse(long now) {
@@ -77,7 +78,7 @@
     }
 
     @VisibleForTesting
-    /* package */ synchronized void clearPointerRecords() {
+    synchronized void clearPointerRecords() {
         pointerRecords.clear();
     }
 
@@ -90,15 +91,16 @@
         return false;
     }
 
+    @Nullable
     public synchronized List<String> getSubtypes() {
         List<String> subtypes = null;
-
         for (MdnsPointerRecord pointerRecord : pointerRecords) {
-            if (pointerRecord.hasSubtype()) {
+            String pointerRecordSubtype = pointerRecord.getSubtype();
+            if (pointerRecordSubtype != null) {
                 if (subtypes == null) {
                     subtypes = new LinkedList<>();
                 }
-                subtypes.add(pointerRecord.getSubtype());
+                subtypes.add(pointerRecordSubtype);
             }
         }
 
@@ -165,7 +167,8 @@
     }
 
     /** Sets the IPv4 address record. */
-    public synchronized boolean setInet4AddressRecord(MdnsInetAddressRecord newInet4AddressRecord) {
+    public synchronized boolean setInet4AddressRecord(
+            @Nullable MdnsInetAddressRecord newInet4AddressRecord) {
         if (recordsAreSame(this.inet4AddressRecord, newInet4AddressRecord)) {
             return false;
         }
@@ -189,7 +192,8 @@
     }
 
     /** Sets the IPv6 address record. */
-    public synchronized boolean setInet6AddressRecord(MdnsInetAddressRecord newInet6AddressRecord) {
+    public synchronized boolean setInet6AddressRecord(
+            @Nullable MdnsInetAddressRecord newInet6AddressRecord) {
         if (recordsAreSame(this.inet6AddressRecord, newInet6AddressRecord)) {
             return false;
         }
@@ -203,6 +207,21 @@
         return true;
     }
 
+    /**
+     * Updates the index of the network interface at which this response was received. Can be set to
+     * {@link MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if unset.
+     */
+    public synchronized void setInterfaceIndex(int interfaceIndex) {
+        this.interfaceIndex = interfaceIndex;
+    }
+
+    /**
+     * Returns the index of the network interface at which this response was received. Can be set to
+     * {@link MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if unset.
+     */
+    public synchronized int getInterfaceIndex() {
+        return interfaceIndex;
+    }
 
     /** Gets the IPv6 address record. */
     public synchronized MdnsInetAddressRecord getInet6AddressRecord() {
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 3e5fc42..6c2bc19 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -25,19 +25,20 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.net.DatagramPacket;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
 /** A class that decodes mDNS responses from UDP packets. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class MdnsResponseDecoder {
 
     public static final int SUCCESS = 0;
     private static final String TAG = "MdnsResponseDecoder";
     private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
-    private final String[] serviceType;
+    private final boolean allowMultipleSrvRecordsPerHost =
+            MdnsConfigs.allowMultipleSrvRecordsPerHost();
+    @Nullable private final String[] serviceType;
     private final Clock clock;
 
     /** Constructs a new decoder that will extract responses for the given service type. */
@@ -92,9 +93,12 @@
      * the responses for completeness; the caller should do that.
      *
      * @param packet The packet to read from.
+     * @param interfaceIndex the network interface index (or {@link
+     *     MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if not known) at which the packet was received
      * @return A list of mDNS responses, or null if the packet contained no appropriate responses.
      */
-    public int decode(@NonNull DatagramPacket packet, @NonNull List<MdnsResponse> responses) {
+    public int decode(@NonNull DatagramPacket packet, @NonNull List<MdnsResponse> responses,
+            int interfaceIndex) {
         MdnsPacketReader reader = new MdnsPacketReader(packet);
 
         List<MdnsRecord> records;
@@ -252,6 +256,9 @@
                         response = new MdnsResponse(now);
                         responses.add(response);
                     }
+                    // Set interface index earlier because some responses have PTR record only.
+                    // Need to know every response is getting from which interface.
+                    response.setInterfaceIndex(interfaceIndex);
                     response.addPointerRecord((MdnsPointerRecord) record);
                 }
             }
@@ -278,11 +285,18 @@
         for (MdnsRecord record : records) {
             if (record instanceof MdnsInetAddressRecord) {
                 MdnsInetAddressRecord inetRecord = (MdnsInetAddressRecord) record;
-                MdnsResponse response = findResponseWithHostName(responses, inetRecord.getName());
-                if (inetRecord.getInet4Address() != null && response != null) {
-                    response.setInet4AddressRecord(inetRecord);
-                } else if (inetRecord.getInet6Address() != null && response != null) {
-                    response.setInet6AddressRecord(inetRecord);
+                if (allowMultipleSrvRecordsPerHost) {
+                    List<MdnsResponse> matchingResponses =
+                            findResponsesWithHostName(responses, inetRecord.getName());
+                    for (MdnsResponse response : matchingResponses) {
+                        assignInetRecord(response, inetRecord);
+                    }
+                } else {
+                    MdnsResponse response =
+                            findResponseWithHostName(responses, inetRecord.getName());
+                    if (response != null) {
+                        assignInetRecord(response, inetRecord);
+                    }
                 }
             }
         }
@@ -290,6 +304,36 @@
         return SUCCESS;
     }
 
+    private static void assignInetRecord(MdnsResponse response, MdnsInetAddressRecord inetRecord) {
+        if (inetRecord.getInet4Address() != null) {
+            response.setInet4AddressRecord(inetRecord);
+        } else if (inetRecord.getInet6Address() != null) {
+            response.setInet6AddressRecord(inetRecord);
+        }
+    }
+
+    private static List<MdnsResponse> findResponsesWithHostName(
+            @Nullable List<MdnsResponse> responses, String[] hostName) {
+        if (responses == null || responses.isEmpty()) {
+            return List.of();
+        }
+
+        List<MdnsResponse> result = null;
+        for (MdnsResponse response : responses) {
+            MdnsServiceRecord serviceRecord = response.getServiceRecord();
+            if (serviceRecord == null) {
+                continue;
+            }
+            if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+                if (result == null) {
+                    result = new ArrayList<>(/* initialCapacity= */ responses.size());
+                }
+                result.add(response);
+            }
+        }
+        return result == null ? List.of() : result;
+    }
+
     public static class Clock {
         public long elapsedRealtime() {
             return SystemClock.elapsedRealtime();
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSearchOptions.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSearchOptions.java
index 6e90d2c..195bc8e 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSearchOptions.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSearchOptions.java
@@ -43,7 +43,7 @@
                 @Override
                 public MdnsSearchOptions createFromParcel(Parcel source) {
                     return new MdnsSearchOptions(source.createStringArrayList(),
-                            source.readBoolean());
+                            source.readBoolean(), source.readBoolean());
                 }
 
                 @Override
@@ -55,14 +55,16 @@
     private final List<String> subtypes;
 
     private final boolean isPassiveMode;
+    private final boolean removeExpiredService;
 
     /** Parcelable constructs for a {@link MdnsServiceInfo}. */
-    MdnsSearchOptions(List<String> subtypes, boolean isPassiveMode) {
+    MdnsSearchOptions(List<String> subtypes, boolean isPassiveMode, boolean removeExpiredService) {
         this.subtypes = new ArrayList<>();
         if (subtypes != null) {
             this.subtypes.addAll(subtypes);
         }
         this.isPassiveMode = isPassiveMode;
+        this.removeExpiredService = removeExpiredService;
     }
 
     /** Returns a {@link Builder} for {@link MdnsSearchOptions}. */
@@ -91,6 +93,11 @@
         return isPassiveMode;
     }
 
+    /** Returns {@code true} if service will be removed after its TTL expires. */
+    public boolean removeExpiredService() {
+        return removeExpiredService;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -100,12 +107,14 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeStringList(subtypes);
         out.writeBoolean(isPassiveMode);
+        out.writeBoolean(removeExpiredService);
     }
 
     /** A builder to create {@link MdnsSearchOptions}. */
     public static final class Builder {
         private final Set<String> subtypes;
         private boolean isPassiveMode = true;
+        private boolean removeExpiredService;
 
         private Builder() {
             subtypes = new ArraySet<>();
@@ -136,8 +145,7 @@
 
         /**
          * Sets if the passive mode scan should be used. The passive mode scans less frequently in
-         * order
-         * to conserve battery and produce less network traffic.
+         * order to conserve battery and produce less network traffic.
          *
          * @param isPassiveMode If set to {@code true}, passive mode will be used. If set to {@code
          *                      false}, active mode will be used.
@@ -147,9 +155,20 @@
             return this;
         }
 
+        /**
+         * Sets if the service should be removed after TTL.
+         *
+         * @param removeExpiredService If set to {@code true}, the service will be removed after TTL
+         */
+        public Builder setRemoveExpiredService(boolean removeExpiredService) {
+            this.removeExpiredService = removeExpiredService;
+            return this;
+        }
+
         /** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */
         public MdnsSearchOptions build() {
-            return new MdnsSearchOptions(new ArrayList<>(subtypes), isPassiveMode);
+            return new MdnsSearchOptions(
+                    new ArrayList<>(subtypes), isPassiveMode, removeExpiredService);
         }
     }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
index 53e58d1..7c19359 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
@@ -28,25 +28,28 @@
 public interface MdnsServiceBrowserListener {
 
     /**
-     * Called when an mDNS service instance is found.
+     * Called when an mDNS service instance is found. This method would be called only if all
+     * service records (PTR, SRV, TXT, A or AAAA) are received .
      *
      * @param serviceInfo The found mDNS service instance.
      */
     void onServiceFound(@NonNull MdnsServiceInfo serviceInfo);
 
     /**
-     * Called when an mDNS service instance is updated.
+     * Called when an mDNS service instance is updated. This method would be called only if all
+     * service records (PTR, SRV, TXT, A or AAAA) are received before.
      *
      * @param serviceInfo The updated mDNS service instance.
      */
     void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo);
 
     /**
-     * Called when an mDNS service instance is no longer valid and removed.
+     * Called when a mDNS service instance is no longer valid and removed. This method would be
+     * called only if all service records (PTR, SRV, TXT, A or AAAA) are received before.
      *
-     * @param serviceInstanceName The service instance name of the removed mDNS service.
+     * @param serviceInfo The service instance of the removed mDNS service.
      */
-    void onServiceRemoved(@NonNull String serviceInstanceName);
+    void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo);
 
     /**
      * Called when searching for mDNS service has stopped because of an error.
@@ -75,4 +78,19 @@
      * @param errorCode            The error code, defined in {@link MdnsResponseErrorCode}.
      */
     void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode);
+
+    /**
+     * Called when a mDNS service instance is discovered. This method would be called if the PTR
+     * record has been received.
+     *
+     * @param serviceInfo The discovered mDNS service instance.
+     */
+    void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo);
+
+    /**
+     * Called when a discovered mDNS service instance is no longer valid and removed.
+     *
+     * @param serviceInfo The service instance of the removed mDNS service.
+     */
+    void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo);
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceInfo.java b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceInfo.java
index 2e4a4e5..9683bc9 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceInfo.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceInfo.java
@@ -17,11 +17,16 @@
 package com.android.server.connectivity.mdns;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.net.module.util.ByteUtils;
+
+import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -34,6 +39,8 @@
  * @hide
  */
 public class MdnsServiceInfo implements Parcelable {
+    private static final Charset US_ASCII = Charset.forName("us-ascii");
+    private static final Charset UTF_8 = Charset.forName("utf-8");
 
     /** @hide */
     public static final Parcelable.Creator<MdnsServiceInfo> CREATOR =
@@ -49,7 +56,9 @@
                             source.readInt(),
                             source.readString(),
                             source.readString(),
-                            source.createStringArrayList());
+                            source.createStringArrayList(),
+                            source.createTypedArrayList(TextEntry.CREATOR),
+                            source.readInt());
                 }
 
                 @Override
@@ -63,10 +72,63 @@
     private final List<String> subtypes;
     private final String[] hostName;
     private final int port;
+    @Nullable
     private final String ipv4Address;
+    @Nullable
     private final String ipv6Address;
-    private final Map<String, String> attributes = new HashMap<>();
-    List<String> textStrings;
+    final List<String> textStrings;
+    @Nullable
+    final List<TextEntry> textEntries;
+    private final int interfaceIndex;
+
+    private final Map<String, byte[]> attributes;
+
+    /** Constructs a {@link MdnsServiceInfo} object with default values. */
+    public MdnsServiceInfo(
+            String serviceInstanceName,
+            String[] serviceType,
+            @Nullable List<String> subtypes,
+            String[] hostName,
+            int port,
+            @Nullable String ipv4Address,
+            @Nullable String ipv6Address,
+            @Nullable List<String> textStrings) {
+        this(
+                serviceInstanceName,
+                serviceType,
+                subtypes,
+                hostName,
+                port,
+                ipv4Address,
+                ipv6Address,
+                textStrings,
+                /* textEntries= */ null,
+                /* interfaceIndex= */ -1);
+    }
+
+    /** Constructs a {@link MdnsServiceInfo} object with default values. */
+    public MdnsServiceInfo(
+            String serviceInstanceName,
+            String[] serviceType,
+            List<String> subtypes,
+            String[] hostName,
+            int port,
+            @Nullable String ipv4Address,
+            @Nullable String ipv6Address,
+            @Nullable List<String> textStrings,
+            @Nullable List<TextEntry> textEntries) {
+        this(
+                serviceInstanceName,
+                serviceType,
+                subtypes,
+                hostName,
+                port,
+                ipv4Address,
+                ipv6Address,
+                textStrings,
+                textEntries,
+                /* interfaceIndex= */ -1);
+    }
 
     /**
      * Constructs a {@link MdnsServiceInfo} object with default values.
@@ -76,12 +138,14 @@
     public MdnsServiceInfo(
             String serviceInstanceName,
             String[] serviceType,
-            List<String> subtypes,
+            @Nullable List<String> subtypes,
             String[] hostName,
             int port,
-            String ipv4Address,
-            String ipv6Address,
-            List<String> textStrings) {
+            @Nullable String ipv4Address,
+            @Nullable String ipv6Address,
+            @Nullable List<String> textStrings,
+            @Nullable List<TextEntry> textEntries,
+            int interfaceIndex) {
         this.serviceInstanceName = serviceInstanceName;
         this.serviceType = serviceType;
         this.subtypes = new ArrayList<>();
@@ -92,72 +156,124 @@
         this.port = port;
         this.ipv4Address = ipv4Address;
         this.ipv6Address = ipv6Address;
+        this.textStrings = new ArrayList<>();
         if (textStrings != null) {
-            for (String text : textStrings) {
-                int pos = text.indexOf('=');
-                if (pos < 1) {
-                    continue;
-                }
-                attributes.put(text.substring(0, pos).toLowerCase(Locale.ENGLISH),
-                        text.substring(++pos));
+            this.textStrings.addAll(textStrings);
+        }
+        this.textEntries = (textEntries == null) ? null : new ArrayList<>(textEntries);
+
+        // The module side sends both {@code textStrings} and {@code textEntries} for backward
+        // compatibility. We should prefer only {@code textEntries} if it's not null.
+        List<TextEntry> entries =
+                (this.textEntries != null) ? this.textEntries : parseTextStrings(this.textStrings);
+        Map<String, byte[]> attributes = new HashMap<>(entries.size());
+        for (TextEntry entry : entries) {
+            String key = entry.getKey().toLowerCase(Locale.ENGLISH);
+
+            // Per https://datatracker.ietf.org/doc/html/rfc6763#section-6.4, only the first entry
+            // of the same key should be accepted:
+            // If a client receives a TXT record containing the same key more than once, then the
+            // client MUST silently ignore all but the first occurrence of that attribute.
+            if (!attributes.containsKey(key)) {
+                attributes.put(key, entry.getValue());
             }
         }
+        this.attributes = Collections.unmodifiableMap(attributes);
+        this.interfaceIndex = interfaceIndex;
     }
 
-    /** @return the name of this service instance. */
+    private static List<TextEntry> parseTextStrings(List<String> textStrings) {
+        List<TextEntry> list = new ArrayList(textStrings.size());
+        for (String textString : textStrings) {
+            TextEntry entry = TextEntry.fromString(textString);
+            if (entry != null) {
+                list.add(entry);
+            }
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    /** Returns the name of this service instance. */
     public String getServiceInstanceName() {
         return serviceInstanceName;
     }
 
-    /** @return the type of this service instance. */
+    /** Returns the type of this service instance. */
     public String[] getServiceType() {
         return serviceType;
     }
 
-    /** @return the list of subtypes supported by this service instance. */
+    /** Returns the list of subtypes supported by this service instance. */
     public List<String> getSubtypes() {
         return new ArrayList<>(subtypes);
     }
 
-    /**
-     * @return {@code true} if this service instance supports any subtypes.
-     * @return {@code false} if this service instance does not support any subtypes.
-     */
+    /** Returns {@code true} if this service instance supports any subtypes. */
     public boolean hasSubtypes() {
         return !subtypes.isEmpty();
     }
 
-    /** @return the host name of this service instance. */
+    /** Returns the host name of this service instance. */
     public String[] getHostName() {
         return hostName;
     }
 
-    /** @return the port number of this service instance. */
+    /** Returns the port number of this service instance. */
     public int getPort() {
         return port;
     }
 
-    /** @return the IPV4 address of this service instance. */
+    /** Returns the IPV4 address of this service instance. */
+    @Nullable
     public String getIpv4Address() {
         return ipv4Address;
     }
 
-    /** @return the IPV6 address of this service instance. */
+    /** Returns the IPV6 address of this service instance. */
+    @Nullable
     public String getIpv6Address() {
         return ipv6Address;
     }
 
     /**
-     * @return the attribute value for {@code key}.
-     * @return {@code null} if no attribute value exists for {@code key}.
+     * Returns the index of the network interface at which this response was received, or -1 if the
+     * index is not known.
      */
+    public int getInterfaceIndex() {
+        return interfaceIndex;
+    }
+
+    /**
+     * Returns attribute value for {@code key} as a UTF-8 string. It's the caller who must make sure
+     * that the value of {@code key} is indeed a UTF-8 string. {@code null} will be returned if no
+     * attribute value exists for {@code key}.
+     */
+    @Nullable
     public String getAttributeByKey(@NonNull String key) {
+        byte[] value = getAttributeAsBytes(key);
+        if (value == null) {
+            return null;
+        }
+        return new String(value, UTF_8);
+    }
+
+    /**
+     * Returns the attribute value for {@code key} as a byte array. {@code null} will be returned if
+     * no attribute value exists for {@code key}.
+     */
+    @Nullable
+    public byte[] getAttributeAsBytes(@NonNull String key) {
         return attributes.get(key.toLowerCase(Locale.ENGLISH));
     }
 
-    /** @return an immutable map of all attributes. */
+    /** Returns an immutable map of all attributes. */
     public Map<String, String> getAttributes() {
-        return Collections.unmodifiableMap(attributes);
+        Map<String, String> map = new HashMap<>(attributes.size());
+        for (Map.Entry<String, byte[]> kv : attributes.entrySet()) {
+            final byte[] value = kv.getValue();
+            map.put(kv.getKey(), value == null ? null : new String(value, UTF_8));
+        }
+        return Collections.unmodifiableMap(map);
     }
 
     @Override
@@ -167,14 +283,6 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        if (textStrings == null) {
-            // Lazily initialize the parcelable field mTextStrings.
-            textStrings = new ArrayList<>(attributes.size());
-            for (Map.Entry<String, String> kv : attributes.entrySet()) {
-                textStrings.add(String.format(Locale.ROOT, "%s=%s", kv.getKey(), kv.getValue()));
-            }
-        }
-
         out.writeString(serviceInstanceName);
         out.writeStringArray(serviceType);
         out.writeStringList(subtypes);
@@ -183,6 +291,8 @@
         out.writeString(ipv4Address);
         out.writeString(ipv6Address);
         out.writeStringList(textStrings);
+        out.writeTypedList(textEntries);
+        out.writeInt(interfaceIndex);
     }
 
     @Override
@@ -195,4 +305,121 @@
                 ipv4Address,
                 port);
     }
+
+
+    /** Represents a DNS TXT key-value pair defined by RFC 6763. */
+    public static final class TextEntry implements Parcelable {
+        public static final Parcelable.Creator<TextEntry> CREATOR =
+                new Parcelable.Creator<TextEntry>() {
+                    @Override
+                    public TextEntry createFromParcel(Parcel source) {
+                        return new TextEntry(source);
+                    }
+
+                    @Override
+                    public TextEntry[] newArray(int size) {
+                        return new TextEntry[size];
+                    }
+                };
+
+        private final String key;
+        private final byte[] value;
+
+        /** Creates a new {@link TextEntry} instance from a '=' separated string. */
+        @Nullable
+        public static TextEntry fromString(String textString) {
+            return fromBytes(textString.getBytes(UTF_8));
+        }
+
+        /** Creates a new {@link TextEntry} instance from a '=' separated byte array. */
+        @Nullable
+        public static TextEntry fromBytes(byte[] textBytes) {
+            int delimitPos = ByteUtils.indexOf(textBytes, (byte) '=');
+
+            // Per https://datatracker.ietf.org/doc/html/rfc6763#section-6.4:
+            // 1. The key MUST be at least one character.  DNS-SD TXT record strings
+            // beginning with an '=' character (i.e., the key is missing) MUST be
+            // silently ignored.
+            // 2. If there is no '=' in a DNS-SD TXT record string, then it is a
+            // boolean attribute, simply identified as being present, with no value.
+            if (delimitPos < 0) {
+                return new TextEntry(new String(textBytes, US_ASCII), (byte[]) null);
+            } else if (delimitPos == 0) {
+                return null;
+            }
+            return new TextEntry(
+                    new String(Arrays.copyOf(textBytes, delimitPos), US_ASCII),
+                    Arrays.copyOfRange(textBytes, delimitPos + 1, textBytes.length));
+        }
+
+        /** Creates a new {@link TextEntry} with given key and value of a UTF-8 string. */
+        public TextEntry(String key, String value) {
+            this(key, value == null ? null : value.getBytes(UTF_8));
+        }
+
+        /** Creates a new {@link TextEntry} with given key and value of a byte array. */
+        public TextEntry(String key, byte[] value) {
+            this.key = key;
+            this.value = value == null ? null : value.clone();
+        }
+
+        private TextEntry(Parcel in) {
+            key = in.readString();
+            value = in.createByteArray();
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public byte[] getValue() {
+            return value == null ? null : value.clone();
+        }
+
+        /** Converts this {@link TextEntry} instance to '=' separated byte array. */
+        public byte[] toBytes() {
+            final byte[] keyBytes = key.getBytes(US_ASCII);
+            if (value == null) {
+                return keyBytes;
+            }
+            return ByteUtils.concat(keyBytes, new byte[]{'='}, value);
+        }
+
+        /** Converts this {@link TextEntry} instance to '=' separated string. */
+        @Override
+        public String toString() {
+            if (value == null) {
+                return key;
+            }
+            return key + "=" + new String(value, UTF_8);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            } else if (!(other instanceof TextEntry)) {
+                return false;
+            }
+            TextEntry otherEntry = (TextEntry) other;
+
+            return key.equals(otherEntry.key) && Arrays.equals(value, otherEntry.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * key.hashCode() + Arrays.hashCode(value);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(key);
+            out.writeByteArray(value);
+        }
+    }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceRecord.java
index 7f54d96..ebd8b77 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
@@ -24,8 +26,6 @@
 import java.util.Objects;
 
 /** An mDNS "SRV" record, which contains service information. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 @VisibleForTesting
 public class MdnsServiceRecord extends MdnsRecord {
     public static final int PROTO_NONE = 0;
@@ -39,7 +39,23 @@
     private String[] serviceHost;
 
     public MdnsServiceRecord(String[] name, MdnsPacketReader reader) throws IOException {
-        super(name, TYPE_SRV, reader);
+        this(name, reader, false);
+    }
+
+    public MdnsServiceRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)
+            throws IOException {
+        super(name, TYPE_SRV, reader, isQuestion);
+    }
+
+    public MdnsServiceRecord(String[] name, long receiptTimeMillis, boolean cacheFlush,
+                    long ttlMillis, int servicePriority, int serviceWeight, int servicePort,
+                    String[] serviceHost) {
+        super(name, TYPE_SRV, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
+                ttlMillis);
+        this.servicePriority = servicePriority;
+        this.serviceWeight = serviceWeight;
+        this.servicePort = servicePort;
+        this.serviceHost = serviceHost;
     }
 
     /** Returns the service's port number. */
@@ -131,7 +147,7 @@
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index e335de9..dd4ff9b 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -16,7 +16,11 @@
 
 package com.android.server.connectivity.mdns;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -25,22 +29,22 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.connectivity.mdns.util.MdnsLogger;
 
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Instance of this class sends and receives mDNS packets of a given service type and invoke
  * registered {@link MdnsServiceBrowserListener} instances.
  */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class MdnsServiceTypeClient {
 
     private static final int DEFAULT_MTU = 1500;
@@ -53,6 +57,12 @@
     private final Object lock = new Object();
     private final Set<MdnsServiceBrowserListener> listeners = new ArraySet<>();
     private final Map<String, MdnsResponse> instanceNameToResponse = new HashMap<>();
+    private final boolean removeServiceAfterTtlExpires =
+            MdnsConfigs.removeServiceAfterTtlExpires();
+    private final boolean allowSearchOptionsToRemoveExpiredService =
+            MdnsConfigs.allowSearchOptionsToRemoveExpiredService();
+
+    @Nullable private MdnsSearchOptions searchOptions;
 
     // The session ID increases when startSendAndReceive() is called where we schedule a
     // QueryTask for
@@ -60,6 +70,7 @@
     private long currentSessionId = 0;
 
     @GuardedBy("lock")
+    @Nullable
     private Future<?> requestTaskFuture;
 
     /**
@@ -80,27 +91,46 @@
 
     private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
             @NonNull MdnsResponse response, @NonNull String[] serviceTypeLabels) {
-        String[] hostName = response.getServiceRecord().getServiceHost();
-        int port = response.getServiceRecord().getServicePort();
+        String[] hostName = null;
+        int port = 0;
+        if (response.hasServiceRecord()) {
+            hostName = response.getServiceRecord().getServiceHost();
+            port = response.getServiceRecord().getServicePort();
+        }
 
         String ipv4Address = null;
         String ipv6Address = null;
         if (response.hasInet4AddressRecord()) {
-            ipv4Address = response.getInet4AddressRecord().getInet4Address().getHostAddress();
+            Inet4Address inet4Address = response.getInet4AddressRecord().getInet4Address();
+            ipv4Address = (inet4Address == null) ? null : inet4Address.getHostAddress();
         }
         if (response.hasInet6AddressRecord()) {
-            ipv6Address = response.getInet6AddressRecord().getInet6Address().getHostAddress();
+            Inet6Address inet6Address = response.getInet6AddressRecord().getInet6Address();
+            ipv6Address = (inet6Address == null) ? null : inet6Address.getHostAddress();
+        }
+        String serviceInstanceName = response.getServiceInstanceName();
+        if (serviceInstanceName == null) {
+            throw new IllegalStateException(
+                    "mDNS response must have non-null service instance name");
+        }
+        List<String> textStrings = null;
+        List<MdnsServiceInfo.TextEntry> textEntries = null;
+        if (response.hasTextRecord()) {
+            textStrings = response.getTextRecord().getStrings();
+            textEntries = response.getTextRecord().getEntries();
         }
         // TODO: Throw an error message if response doesn't have Inet6 or Inet4 address.
         return new MdnsServiceInfo(
-                response.getServiceInstanceName(),
+                serviceInstanceName,
                 serviceTypeLabels,
                 response.getSubtypes(),
                 hostName,
                 port,
                 ipv4Address,
                 ipv6Address,
-                response.getTextRecord().getStrings());
+                textStrings,
+                textEntries,
+                response.getInterfaceIndex());
     }
 
     /**
@@ -115,13 +145,14 @@
             @NonNull MdnsServiceBrowserListener listener,
             @NonNull MdnsSearchOptions searchOptions) {
         synchronized (lock) {
-            if (!listeners.contains(listener)) {
-                listeners.add(listener);
+            this.searchOptions = searchOptions;
+            if (listeners.add(listener)) {
                 for (MdnsResponse existingResponse : instanceNameToResponse.values()) {
+                    final MdnsServiceInfo info =
+                            buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
+                    listener.onServiceNameDiscovered(info);
                     if (existingResponse.isComplete()) {
-                        listener.onServiceFound(
-                                buildMdnsServiceInfoFromResponse(existingResponse,
-                                        serviceTypeLabels));
+                        listener.onServiceFound(info);
                     }
                 }
             }
@@ -164,10 +195,23 @@
     }
 
     public synchronized void processResponse(@NonNull MdnsResponse response) {
-        if (response.isGoodbye()) {
-            onGoodbyeReceived(response.getServiceInstanceName());
+        if (shouldRemoveServiceAfterTtlExpires()) {
+            // Because {@link QueryTask} and {@link processResponse} are running in different
+            // threads. We need to synchronize {@link lock} to protect
+            // {@link instanceNameToResponse} won’t be modified at the same time.
+            synchronized (lock) {
+                if (response.isGoodbye()) {
+                    onGoodbyeReceived(response.getServiceInstanceName());
+                } else {
+                    onResponseReceived(response);
+                }
+            }
         } else {
-            onResponseReceived(response);
+            if (response.isGoodbye()) {
+                onGoodbyeReceived(response.getServiceInstanceName());
+            } else {
+                onResponseReceived(response);
+            }
         }
     }
 
@@ -183,14 +227,21 @@
 
         boolean newServiceFound = false;
         boolean existingServiceChanged = false;
+        boolean serviceBecomesComplete = false;
         if (currentResponse == null) {
             newServiceFound = true;
             currentResponse = response;
-            instanceNameToResponse.put(response.getServiceInstanceName(), currentResponse);
-        } else if (currentResponse.mergeRecordsFrom(response)) {
-            existingServiceChanged = true;
+            String serviceInstanceName = response.getServiceInstanceName();
+            if (serviceInstanceName != null) {
+                instanceNameToResponse.put(serviceInstanceName, currentResponse);
+            }
+        } else {
+            boolean before = currentResponse.isComplete();
+            existingServiceChanged = currentResponse.mergeRecordsFrom(response);
+            boolean after = currentResponse.isComplete();
+            serviceBecomesComplete = !before && after;
         }
-        if (!currentResponse.isComplete() || (!newServiceFound && !existingServiceChanged)) {
+        if (!newServiceFound && !existingServiceChanged) {
             return;
         }
         MdnsServiceInfo serviceInfo =
@@ -198,18 +249,41 @@
 
         for (MdnsServiceBrowserListener listener : listeners) {
             if (newServiceFound) {
-                listener.onServiceFound(serviceInfo);
-            } else {
-                listener.onServiceUpdated(serviceInfo);
+                listener.onServiceNameDiscovered(serviceInfo);
+            }
+
+            if (currentResponse.isComplete()) {
+                if (newServiceFound || serviceBecomesComplete) {
+                    listener.onServiceFound(serviceInfo);
+                } else {
+                    listener.onServiceUpdated(serviceInfo);
+                }
             }
         }
     }
 
-    private void onGoodbyeReceived(@NonNull String serviceInstanceName) {
-        instanceNameToResponse.remove(serviceInstanceName);
-        for (MdnsServiceBrowserListener listener : listeners) {
-            listener.onServiceRemoved(serviceInstanceName);
+    private void onGoodbyeReceived(@Nullable String serviceInstanceName) {
+        final MdnsResponse response = instanceNameToResponse.remove(serviceInstanceName);
+        if (response == null) {
+            return;
         }
+        for (MdnsServiceBrowserListener listener : listeners) {
+            final MdnsServiceInfo serviceInfo =
+                    buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
+            if (response.isComplete()) {
+                listener.onServiceRemoved(serviceInfo);
+            }
+            listener.onServiceNameRemoved(serviceInfo);
+        }
+    }
+
+    private boolean shouldRemoveServiceAfterTtlExpires() {
+        if (removeServiceAfterTtlExpires) {
+            return true;
+        }
+        return allowSearchOptionsToRemoveExpiredService
+                && searchOptions != null
+                && searchOptions.removeExpiredService();
     }
 
     @VisibleForTesting
@@ -332,7 +406,7 @@
                                 config.expectUnicastResponse,
                                 config.transactionId)
                                 .call();
-            } catch (Exception e) {
+            } catch (RuntimeException e) {
                 LOGGER.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
                         TextUtils.join(",", config.subtypes)), e);
                 result = null;
@@ -359,11 +433,36 @@
                         listener.onDiscoveryQuerySent(result.second, result.first);
                     }
                 }
+                if (shouldRemoveServiceAfterTtlExpires()) {
+                    Iterator<MdnsResponse> iter = instanceNameToResponse.values().iterator();
+                    while (iter.hasNext()) {
+                        MdnsResponse existingResponse = iter.next();
+                        if (existingResponse.hasServiceRecord()
+                                && existingResponse
+                                .getServiceRecord()
+                                .getRemainingTTL(SystemClock.elapsedRealtime())
+                                == 0) {
+                            iter.remove();
+                            for (MdnsServiceBrowserListener listener : listeners) {
+                                String serviceInstanceName =
+                                        existingResponse.getServiceInstanceName();
+                                if (serviceInstanceName != null) {
+                                    final MdnsServiceInfo serviceInfo =
+                                            buildMdnsServiceInfoFromResponse(
+                                                    existingResponse, serviceTypeLabels);
+                                    if (existingResponse.isComplete()) {
+                                        listener.onServiceRemoved(serviceInfo);
+                                    }
+                                    listener.onServiceNameRemoved(serviceInfo);
+                                }
+                            }
+                        }
+                    }
+                }
                 QueryTaskConfig config = this.config.getConfigForNextRun();
                 requestTaskFuture =
                         executor.schedule(
-                                new QueryTask(config), config.timeToRunNextTaskInMs,
-                                TimeUnit.MILLISECONDS);
+                                new QueryTask(config), config.timeToRunNextTaskInMs, MILLISECONDS);
             }
         }
     }
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
index 34db7f0..0a9b2fc 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocket.java
@@ -19,11 +19,13 @@
 import android.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsLogger;
 
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.InetSocketAddress;
 import java.net.MulticastSocket;
+import java.net.SocketException;
 import java.util.List;
 
 /**
@@ -32,23 +34,30 @@
  *
  * @see MulticastSocket for javadoc of each public method.
  */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class MdnsSocket {
+    private static final MdnsLogger LOGGER = new MdnsLogger("MdnsSocket");
+
+    static final int INTERFACE_INDEX_UNSPECIFIED = -1;
     private static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
             new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
     private static final InetSocketAddress MULTICAST_IPV6_ADDRESS =
             new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
-    private static boolean isOnIPv6OnlyNetwork = false;
     private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider;
     private final MulticastSocket multicastSocket;
+    private boolean isOnIPv6OnlyNetwork;
 
     public MdnsSocket(
             @NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, int port)
             throws IOException {
+        this(multicastNetworkInterfaceProvider, new MulticastSocket(port));
+    }
+
+    @VisibleForTesting
+    MdnsSocket(@NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider,
+            MulticastSocket multicastSocket) throws IOException {
         this.multicastNetworkInterfaceProvider = multicastNetworkInterfaceProvider;
         this.multicastNetworkInterfaceProvider.startWatchingConnectivityChanges();
-        multicastSocket = createMulticastSocket(port);
+        this.multicastSocket = multicastSocket;
         // RFC Spec: https://tools.ietf.org/html/rfc6762
         // Time to live is set 255, which is similar to the jMDNS implementation.
         multicastSocket.setTimeToLive(255);
@@ -103,9 +112,17 @@
         multicastNetworkInterfaceProvider.stopWatchingConnectivityChanges();
     }
 
-    @VisibleForTesting
-    MulticastSocket createMulticastSocket(int port) throws IOException {
-        return new MulticastSocket(port);
+    /**
+     * Returns the index of the network interface that this socket is bound to. If the interface
+     * cannot be determined, returns -1.
+     */
+    public int getInterfaceIndex() {
+        try {
+            return multicastSocket.getNetworkInterface().getIndex();
+        } catch (SocketException e) {
+            LOGGER.e("Failed to retrieve interface index for socket.", e);
+            return -1;
+        }
     }
 
     public boolean isOnIPv6OnlyNetwork() {
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 010f761..758221a 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -46,8 +46,6 @@
  *
  * <p>See https://tools.ietf.org/html/rfc6763 (namely sections 4 and 5).
  */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
 public class MdnsSocketClient {
 
     private static final String TAG = "MdnsClient";
@@ -71,7 +69,7 @@
     final Queue<DatagramPacket> unicastPacketQueue = new ArrayDeque<>();
     private final Context context;
     private final byte[] multicastReceiverBuffer = new byte[RECEIVER_BUFFER_SIZE];
-    private final byte[] unicastReceiverBuffer;
+    @Nullable private final byte[] unicastReceiverBuffer;
     private final MdnsResponseDecoder responseDecoder;
     private final MulticastLock multicastLock;
     private final boolean useSeparateSocketForUnicast =
@@ -79,6 +77,8 @@
     private final boolean checkMulticastResponse = MdnsConfigs.checkMulticastResponse();
     private final long checkMulticastResponseIntervalMs =
             MdnsConfigs.checkMulticastResponseIntervalMs();
+    private final boolean propagateInterfaceIndex =
+            MdnsConfigs.allowNetworkInterfaceIndexPropagation();
     private final Object socketLock = new Object();
     private final Object timerObject = new Object();
     // If multicast response was received in the current session. The value is reset in the
@@ -92,20 +92,17 @@
     // If the phone is the bad state where it can't receive any multicast response.
     @VisibleForTesting
     AtomicBoolean cannotReceiveMulticastResponse = new AtomicBoolean(false);
-    @VisibleForTesting
-    volatile Thread sendThread;
-    @VisibleForTesting
-    Thread multicastReceiveThread;
-    @VisibleForTesting
-    Thread unicastReceiveThread;
+    @VisibleForTesting @Nullable volatile Thread sendThread;
+    @VisibleForTesting @Nullable Thread multicastReceiveThread;
+    @VisibleForTesting @Nullable Thread unicastReceiveThread;
     private volatile boolean shouldStopSocketLoop;
-    private Callback callback;
-    private MdnsSocket multicastSocket;
-    private MdnsSocket unicastSocket;
+    @Nullable private Callback callback;
+    @Nullable private MdnsSocket multicastSocket;
+    @Nullable private MdnsSocket unicastSocket;
     private int receivedPacketNumber = 0;
-    private Timer logMdnsPacketTimer;
+    @Nullable private Timer logMdnsPacketTimer;
     private AtomicInteger packetsCount;
-    private Timer checkMulticastResponseTimer;
+    @Nullable private Timer checkMulticastResponseTimer;
 
     public MdnsSocketClient(@NonNull Context context, @NonNull MulticastLock multicastLock) {
         this.context = context;
@@ -246,7 +243,12 @@
 
         if (useSeparateSocketForUnicast) {
             unicastReceiveThread =
-                    new Thread(() -> receiveThreadMain(unicastReceiverBuffer, unicastSocket));
+                    new Thread(
+                            () -> {
+                                if (unicastReceiverBuffer != null) {
+                                    receiveThreadMain(unicastReceiverBuffer, unicastSocket);
+                                }
+                            });
             unicastReceiveThread.setName("mdns-unicast-receive");
             unicastReceiveThread.start();
         }
@@ -325,11 +327,15 @@
                             unicastPacketsToSend.addAll(unicastPacketQueue);
                             unicastPacketQueue.clear();
                         }
+                        if (unicastSocket != null) {
+                            sendPackets(unicastPacketsToSend, unicastSocket);
+                        }
                     }
 
-                    // Send all the packets.
-                    sendPackets(multicastPacketsToSend, multicastSocket);
-                    sendPackets(unicastPacketsToSend, unicastSocket);
+                    // Send multicast packets.
+                    if (multicastSocket != null) {
+                        sendPackets(multicastPacketsToSend, multicastSocket);
+                    }
 
                     // Sleep ONLY if no more packets have been added to the queue, while packets
                     // were being sent.
@@ -349,7 +355,9 @@
         } finally {
             LOGGER.log("Send thread stopped.");
             try {
-                multicastSocket.leaveGroup();
+                if (multicastSocket != null) {
+                    multicastSocket.leaveGroup();
+                }
             } catch (Exception t) {
                 LOGGER.e("Failed to leave the group.", t);
             }
@@ -357,17 +365,19 @@
             // Close the socket first. This is the only way to interrupt a blocking receive.
             try {
                 // This is a race with the use of the file descriptor (b/27403984).
-                multicastSocket.close();
+                if (multicastSocket != null) {
+                    multicastSocket.close();
+                }
                 if (unicastSocket != null) {
                     unicastSocket.close();
                 }
-            } catch (Exception t) {
+            } catch (RuntimeException t) {
                 LOGGER.e("Failed to close the mdns socket.", t);
             }
         }
     }
 
-    private void receiveThreadMain(byte[] receiverBuffer, MdnsSocket socket) {
+    private void receiveThreadMain(byte[] receiverBuffer, @Nullable MdnsSocket socket) {
         DatagramPacket packet = new DatagramPacket(receiverBuffer, receiverBuffer.length);
 
         while (!shouldStopSocketLoop) {
@@ -382,7 +392,12 @@
 
                 if (!shouldStopSocketLoop) {
                     String responseType = socket == multicastSocket ? MULTICAST_TYPE : UNICAST_TYPE;
-                    processResponsePacket(packet, responseType);
+                    processResponsePacket(
+                            packet,
+                            responseType,
+                            /* interfaceIndex= */ (socket == null || !propagateInterfaceIndex)
+                                    ? MdnsSocket.INTERFACE_INDEX_UNSPECIFIED
+                                    : socket.getInterfaceIndex());
                 }
             } catch (IOException e) {
                 if (!shouldStopSocketLoop) {
@@ -393,12 +408,12 @@
         LOGGER.log("Receive thread stopped.");
     }
 
-    private int processResponsePacket(@NonNull DatagramPacket packet, String responseType)
-            throws IOException {
+    private int processResponsePacket(
+            @NonNull DatagramPacket packet, String responseType, int interfaceIndex) {
         int packetNumber = ++receivedPacketNumber;
 
         List<MdnsResponse> responses = new LinkedList<>();
-        int errorCode = responseDecoder.decode(packet, responses);
+        int errorCode = responseDecoder.decode(packet, responses, interfaceIndex);
         if (errorCode == MdnsResponseDecoder.SUCCESS) {
             if (responseType.equals(MULTICAST_TYPE)) {
                 receivedMulticastResponse = true;
@@ -414,7 +429,8 @@
             }
             for (MdnsResponse response : responses) {
                 String serviceInstanceName = response.getServiceInstanceName();
-                LOGGER.log("mDNS %s response received: %s", responseType, serviceInstanceName);
+                LOGGER.log("mDNS %s response received: %s at ifIndex %d", responseType,
+                        serviceInstanceName, interfaceIndex);
                 if (callback != null) {
                     callback.onResponseReceived(response);
                 }
@@ -492,7 +508,7 @@
     }
 
     public boolean isOnIPv6OnlyNetwork() {
-        return multicastSocket.isOnIPv6OnlyNetwork();
+        return multicastSocket != null && multicastSocket.isOnIPv6OnlyNetwork();
     }
 
     /** Callback for {@link MdnsSocketClient}. */
diff --git a/service/mdns/com/android/server/connectivity/mdns/MdnsTextRecord.java b/service/mdns/com/android/server/connectivity/mdns/MdnsTextRecord.java
index a364560..4149dbe 100644
--- a/service/mdns/com/android/server/connectivity/mdns/MdnsTextRecord.java
+++ b/service/mdns/com/android/server/connectivity/mdns/MdnsTextRecord.java
@@ -16,7 +16,10 @@
 
 package com.android.server.connectivity.mdns;
 
+import android.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -24,35 +27,57 @@
 import java.util.List;
 import java.util.Objects;
 
-/** An mDNS "TXT" record, which contains a list of text strings. */
-// TODO(b/242631897): Resolve nullness suppression.
-@SuppressWarnings("nullness")
+/** An mDNS "TXT" record, which contains a list of {@link TextEntry}. */
 @VisibleForTesting
 public class MdnsTextRecord extends MdnsRecord {
-    private List<String> strings;
+    private List<TextEntry> entries;
 
     public MdnsTextRecord(String[] name, MdnsPacketReader reader) throws IOException {
-        super(name, TYPE_TXT, reader);
+        this(name, reader, false);
+    }
+
+    public MdnsTextRecord(String[] name, MdnsPacketReader reader, boolean isQuestion)
+            throws IOException {
+        super(name, TYPE_TXT, reader, isQuestion);
+    }
+
+    public MdnsTextRecord(String[] name, long receiptTimeMillis, boolean cacheFlush, long ttlMillis,
+            List<TextEntry> entries) {
+        super(name, TYPE_TXT, MdnsConstants.QCLASS_INTERNET, receiptTimeMillis, cacheFlush,
+                ttlMillis);
+        this.entries = entries;
     }
 
     /** Returns the list of strings. */
     public List<String> getStrings() {
-        return Collections.unmodifiableList(strings);
+        final List<String> list = new ArrayList<>(entries.size());
+        for (TextEntry entry : entries) {
+            list.add(entry.toString());
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    /** Returns the list of TXT key-value pairs. */
+    public List<TextEntry> getEntries() {
+        return Collections.unmodifiableList(entries);
     }
 
     @Override
     protected void readData(MdnsPacketReader reader) throws IOException {
-        strings = new ArrayList<>();
+        entries = new ArrayList<>();
         while (reader.getRemaining() > 0) {
-            strings.add(reader.readString());
+            TextEntry entry = reader.readTextEntry();
+            if (entry != null) {
+                entries.add(entry);
+            }
         }
     }
 
     @Override
     protected void writeData(MdnsPacketWriter writer) throws IOException {
-        if (strings != null) {
-            for (String string : strings) {
-                writer.writeString(string);
+        if (entries != null) {
+            for (TextEntry entry : entries) {
+                writer.writeTextEntry(entry);
             }
         }
     }
@@ -61,9 +86,9 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("TXT: {");
-        if (strings != null) {
-            for (String string : strings) {
-                sb.append(' ').append(string);
+        if (entries != null) {
+            for (TextEntry entry : entries) {
+                sb.append(' ').append(entry);
             }
         }
         sb.append("}");
@@ -73,11 +98,11 @@
 
     @Override
     public int hashCode() {
-        return (super.hashCode() * 31) + Objects.hash(strings);
+        return (super.hashCode() * 31) + Objects.hash(entries);
     }
 
     @Override
-    public boolean equals(Object other) {
+    public boolean equals(@Nullable Object other) {
         if (this == other) {
             return true;
         }
@@ -85,6 +110,6 @@
             return false;
         }
 
-        return super.equals(other) && Objects.equals(strings, ((MdnsTextRecord) other).strings);
+        return super.equals(other) && Objects.equals(entries, ((MdnsTextRecord) other).entries);
     }
 }
\ No newline at end of file
diff --git a/service/mdns/com/android/server/connectivity/mdns/util/MdnsLogger.java b/service/mdns/com/android/server/connectivity/mdns/util/MdnsLogger.java
index 431f1fd..63107e5 100644
--- a/service/mdns/com/android/server/connectivity/mdns/util/MdnsLogger.java
+++ b/service/mdns/com/android/server/connectivity/mdns/util/MdnsLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity.mdns.util;
 
+import android.annotation.Nullable;
 import android.text.TextUtils;
 
 import com.android.net.module.util.SharedLog;
@@ -40,7 +41,7 @@
         mLog.log(message);
     }
 
-    public void log(String message, Object... args) {
+    public void log(String message, @Nullable Object... args) {
         mLog.log(message + " ; " + TextUtils.join(" ; ", args));
     }
 
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index a26d1e6..8f6df21 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -181,9 +181,13 @@
     return netdutils::status::ok;
 }
 
-Status TrafficController::start() {
+Status TrafficController::start(bool startSkDestroyListener) {
     RETURN_IF_NOT_OK(initMaps());
 
+    if (!startSkDestroyListener) {
+        return netdutils::status::ok;
+    }
+
     auto result = makeSkDestroyListener();
     if (!isOk(result)) {
         ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
@@ -591,7 +595,7 @@
     }
 }
 
-void TrafficController::dump(int fd, bool verbose) {
+void TrafficController::dump(int fd, bool verbose __unused) {
     std::lock_guard guard(mMutex);
     DumpWriter dw(fd);
 
@@ -619,157 +623,6 @@
                getMapStatus(mConfigurationMap.getMap(), CONFIGURATION_MAP_PATH).c_str());
     dw.println("mUidOwnerMap status: %s",
                getMapStatus(mUidOwnerMap.getMap(), UID_OWNER_MAP_PATH).c_str());
-
-    if (!verbose) {
-        return;
-    }
-
-    dw.blankline();
-    dw.println("BPF map content:");
-
-    ScopedIndent indentForMapContent(dw);
-
-    // Print CookieTagMap content.
-    // TagSocketTest in CTS was using the output of mCookieTagMap dump.
-    // So, mCookieTagMap dump can not be removed until the previous CTS support period is over.
-    dumpBpfMap("mCookieTagMap", dw, "");
-    const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
-                                          const BpfMap<uint64_t, UidTagValue>&) {
-        dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
-        return base::Result<void>();
-    };
-    base::Result<void> res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
-    if (!res.ok()) {
-        dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
-    }
-
-
-    // Print uidStatsMap content.
-    std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
-                                           " rxPackets txBytes txPackets");
-    dumpBpfMap("mStatsMapA", dw, statsHeader);
-    const auto printStatsInfo = [&dw, this](const StatsKey& key, const StatsValue& value,
-                                            const BpfMap<StatsKey, StatsValue>&) {
-        uint32_t ifIndex = key.ifaceIndex;
-        auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
-        if (!ifname.ok()) {
-            ifname = IfaceValue{"unknown"};
-        }
-        dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
-                   ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
-                   value.rxPackets, value.txBytes, value.txPackets);
-        return base::Result<void>();
-    };
-    res = mStatsMapA.iterateWithValue(printStatsInfo);
-    if (!res.ok()) {
-        dw.println("mStatsMapA print end with error: %s", res.error().message().c_str());
-    }
-
-    // Print TagStatsMap content.
-    dumpBpfMap("mStatsMapB", dw, statsHeader);
-    res = mStatsMapB.iterateWithValue(printStatsInfo);
-    if (!res.ok()) {
-        dw.println("mStatsMapB print end with error: %s", res.error().message().c_str());
-    }
-
-    // Print ifaceIndexToNameMap content.
-    dumpBpfMap("mIfaceIndexNameMap", dw, "");
-    const auto printIfaceNameInfo = [&dw](const uint32_t& key, const IfaceValue& value,
-                                          const BpfMap<uint32_t, IfaceValue>&) {
-        const char* ifname = value.name;
-        dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
-        return base::Result<void>();
-    };
-    res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
-    if (!res.ok()) {
-        dw.println("mIfaceIndexNameMap print end with error: %s", res.error().message().c_str());
-    }
-
-    // Print ifaceStatsMap content
-    std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
-                                                " txPackets");
-    dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
-    const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
-                                                 const BpfMap<uint32_t, StatsValue>&) {
-        auto ifname = mIfaceIndexNameMap.readValue(key);
-        if (!ifname.ok()) {
-            ifname = IfaceValue{"unknown"};
-        }
-        dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
-                   value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
-        return base::Result<void>();
-    };
-    res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
-    if (!res.ok()) {
-        dw.println("mIfaceStatsMap print end with error: %s", res.error().message().c_str());
-    }
-
-    dw.blankline();
-
-    uint32_t key = UID_RULES_CONFIGURATION_KEY;
-    auto configuration = mConfigurationMap.readValue(key);
-    if (configuration.ok()) {
-        dw.println("current ownerMatch configuration: %d%s", configuration.value(),
-                   uidMatchTypeToString(configuration.value()).c_str());
-    } else {
-        dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
-                   configuration.error().message().c_str());
-    }
-
-    key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
-    configuration = mConfigurationMap.readValue(key);
-    if (configuration.ok()) {
-        const char* statsMapDescription = "???";
-        switch (configuration.value()) {
-            case SELECT_MAP_A:
-                statsMapDescription = "SELECT_MAP_A";
-                break;
-            case SELECT_MAP_B:
-                statsMapDescription = "SELECT_MAP_B";
-                break;
-                // No default clause, so if we ever add a third map, this code will fail to build.
-        }
-        dw.println("current statsMap configuration: %d %s", configuration.value(),
-                   statsMapDescription);
-    } else {
-        dw.println("mConfigurationMap read stats map configure failed with error: %s",
-                   configuration.error().message().c_str());
-    }
-    dumpBpfMap("mUidOwnerMap", dw, "");
-    const auto printUidMatchInfo = [&dw, this](const uint32_t& key, const UidOwnerValue& value,
-                                               const BpfMap<uint32_t, UidOwnerValue>&) {
-        if (value.rule & IIF_MATCH) {
-            auto ifname = mIfaceIndexNameMap.readValue(value.iif);
-            if (ifname.ok()) {
-                dw.println("%u %s %s", key, uidMatchTypeToString(value.rule).c_str(),
-                           ifname.value().name);
-            } else {
-                dw.println("%u %s %u", key, uidMatchTypeToString(value.rule).c_str(), value.iif);
-            }
-        } else {
-            dw.println("%u %s", key, uidMatchTypeToString(value.rule).c_str());
-        }
-        return base::Result<void>();
-    };
-    res = mUidOwnerMap.iterateWithValue(printUidMatchInfo);
-    if (!res.ok()) {
-        dw.println("mUidOwnerMap print end with error: %s", res.error().message().c_str());
-    }
-    dumpBpfMap("mUidPermissionMap", dw, "");
-    const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
-                                              const BpfMap<uint32_t, uint8_t>&) {
-        dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
-        return base::Result<void>();
-    };
-    res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
-    if (!res.ok()) {
-        dw.println("mUidPermissionMap print end with error: %s", res.error().message().c_str());
-    }
-
-    dumpBpfMap("mPrivilegedUser", dw, "");
-    for (uid_t uid : mPrivilegedUser) {
-        dw.println("%u ALLOW_UPDATE_DEVICE_STATS", (uint32_t)uid);
-    }
 }
 
 }  // namespace net
diff --git a/service/native/TrafficControllerTest.cpp b/service/native/TrafficControllerTest.cpp
index d08ffee..57f32af 100644
--- a/service/native/TrafficControllerTest.cpp
+++ b/service/native/TrafficControllerTest.cpp
@@ -59,8 +59,6 @@
 constexpr uid_t TEST_UID3 = 98765;
 constexpr uint32_t TEST_TAG = 42;
 constexpr uint32_t TEST_COUNTERSET = 1;
-constexpr int TEST_COOKIE = 1;
-constexpr char TEST_IFNAME[] = "test0";
 constexpr int TEST_IFINDEX = 999;
 constexpr int RXPACKETS = 1;
 constexpr int RXBYTES = 100;
@@ -770,116 +768,6 @@
     expectPrivilegedUserSetEmpty();
 }
 
-TEST_F(TrafficControllerTest, TestDumpsys) {
-    StatsKey tagStatsMapKey;
-    populateFakeStats(TEST_COOKIE, TEST_UID, TEST_TAG, &tagStatsMapKey);
-    populateFakeCounterSet(TEST_UID3, TEST_COUNTERSET);
-
-    // Expect: (part of this depends on hard-code values in populateFakeStats())
-    //
-    // mCookieTagMap:
-    // cookie=1 tag=0x2a uid=10086
-    //
-    // mUidCounterSetMap:
-    // 98765 1
-    //
-    // mAppUidStatsMap::
-    // uid rxBytes rxPackets txBytes txPackets
-    // 10086 100 1 0 0
-    //
-    // mStatsMapA:
-    // ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets
-    // 999 test0 0x2a 10086 1 100 1 0 0
-    std::vector<std::string> expectedLines = {
-        "mCookieTagMap:",
-        fmt::format("cookie={} tag={:#x} uid={}", TEST_COOKIE, TEST_TAG, TEST_UID),
-        "mStatsMapA",
-        "ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets",
-        fmt::format("{} {} {:#x} {} {} {} {} {} {}",
-                    TEST_IFINDEX, TEST_IFNAME, TEST_TAG, TEST_UID, TEST_COUNTERSET, RXBYTES,
-                    RXPACKETS, TXBYTES, TXPACKETS)};
-
-    populateFakeIfaceIndexName(TEST_IFNAME, TEST_IFINDEX);
-    expectedLines.emplace_back("mIfaceIndexNameMap:");
-    expectedLines.emplace_back(fmt::format("ifaceIndex={} ifaceName={}",
-                                           TEST_IFINDEX, TEST_IFNAME));
-
-    ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, HAPPY_BOX_MATCH,
-                                        TrafficController::IptOpInsert)));
-    expectedLines.emplace_back("mUidOwnerMap:");
-    expectedLines.emplace_back(fmt::format("{}  HAPPY_BOX_MATCH", TEST_UID));
-
-    mTc.setPermissionForUids(INetd::PERMISSION_UPDATE_DEVICE_STATS, {TEST_UID2});
-    expectedLines.emplace_back("mUidPermissionMap:");
-    expectedLines.emplace_back(fmt::format("{}  BPF_PERMISSION_UPDATE_DEVICE_STATS", TEST_UID2));
-    expectedLines.emplace_back("mPrivilegedUser:");
-    expectedLines.emplace_back(fmt::format("{} ALLOW_UPDATE_DEVICE_STATS", TEST_UID2));
-    EXPECT_TRUE(expectDumpsysContains(expectedLines));
-}
-
-TEST_F(TrafficControllerTest, dumpsysInvalidMaps) {
-    makeTrafficControllerMapsInvalid();
-
-    const std::string kErrIterate = "print end with error: Get firstKey map -1 failed: "
-            "Bad file descriptor";
-    const std::string kErrReadRulesConfig = "read ownerMatch configure failed with error: "
-            "Read value of map -1 failed: Bad file descriptor";
-    const std::string kErrReadStatsMapConfig = "read stats map configure failed with error: "
-            "Read value of map -1 failed: Bad file descriptor";
-
-    std::vector<std::string> expectedLines = {
-        fmt::format("mCookieTagMap {}", kErrIterate),
-        fmt::format("mStatsMapA {}", kErrIterate),
-        fmt::format("mStatsMapB {}", kErrIterate),
-        fmt::format("mIfaceIndexNameMap {}", kErrIterate),
-        fmt::format("mIfaceStatsMap {}", kErrIterate),
-        fmt::format("mConfigurationMap {}", kErrReadRulesConfig),
-        fmt::format("mConfigurationMap {}", kErrReadStatsMapConfig),
-        fmt::format("mUidOwnerMap {}", kErrIterate),
-        fmt::format("mUidPermissionMap {}", kErrIterate)};
-    EXPECT_TRUE(expectDumpsysContains(expectedLines));
-}
-
-TEST_F(TrafficControllerTest, uidMatchTypeToString) {
-    // NO_MATCH(0) can't be verified because match type flag is added by OR operator.
-    // See TrafficController::addRule()
-    static const struct TestConfig {
-        UidOwnerMatchType uidOwnerMatchType;
-        std::string expected;
-    } testConfigs[] = {
-            // clang-format off
-            {HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"},
-            {DOZABLE_MATCH, "DOZABLE_MATCH"},
-            {STANDBY_MATCH, "STANDBY_MATCH"},
-            {POWERSAVE_MATCH, "POWERSAVE_MATCH"},
-            {HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"},
-            {RESTRICTED_MATCH, "RESTRICTED_MATCH"},
-            {LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH"},
-            {IIF_MATCH, "IIF_MATCH"},
-            {LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH"},
-            {OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH"},
-            {OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH"},
-            {OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH"},
-            // clang-format on
-    };
-
-    for (const auto& config : testConfigs) {
-        SCOPED_TRACE(fmt::format("testConfig: [{}, {}]", config.uidOwnerMatchType,
-                     config.expected));
-
-        // Test private function uidMatchTypeToString() via dumpsys.
-        ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, config.uidOwnerMatchType,
-                                            TrafficController::IptOpInsert)));
-        std::vector<std::string> expectedLines;
-        expectedLines.emplace_back(fmt::format("{}  {}", TEST_UID, config.expected));
-        EXPECT_TRUE(expectDumpsysContains(expectedLines));
-
-        // Clean up the stubs.
-        ASSERT_TRUE(isOk(updateUidOwnerMaps({TEST_UID}, config.uidOwnerMatchType,
-                                            TrafficController::IptOpDelete)));
-    }
-}
-
 TEST_F(TrafficControllerTest, getFirewallType) {
     static const struct TestConfig {
         ChildChain childChain;
diff --git a/service/native/include/TrafficController.h b/service/native/include/TrafficController.h
index 8512929..b44d795 100644
--- a/service/native/include/TrafficController.h
+++ b/service/native/include/TrafficController.h
@@ -38,7 +38,7 @@
     /*
      * Initialize the whole controller
      */
-    netdutils::Status start();
+    netdutils::Status start(bool startSkDestroyListener);
 
     /*
      * Swap the stats map config from current active stats map to the idle one.
diff --git a/service/native/libs/libclat/clatutils.cpp b/service/native/libs/libclat/clatutils.cpp
index 4a125ba..be86612 100644
--- a/service/native/libs/libclat/clatutils.cpp
+++ b/service/native/libs/libclat/clatutils.cpp
@@ -126,10 +126,19 @@
 
 // Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
 int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix,
-                        in6_addr* v6) {
+                        in6_addr* v6, uint32_t mark) {
     int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
     if (s == -1) return -errno;
 
+    // Socket's mark affects routing decisions (network selection)
+    // An fwmark is necessary for clat to bypass the VPN during initialization.
+    if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) {
+        int ret = errno;
+        ALOGE("setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno));
+        close(s);
+        return -ret;
+    }
+
     if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) == -1) {
         close(s);
         return -errno;
diff --git a/service/native/libs/libclat/clatutils_test.cpp b/service/native/libs/libclat/clatutils_test.cpp
index 8cca1f4..abd4e81 100644
--- a/service/native/libs/libclat/clatutils_test.cpp
+++ b/service/native/libs/libclat/clatutils_test.cpp
@@ -202,7 +202,8 @@
     in6_addr v6;
     EXPECT_EQ(1, inet_pton(AF_INET6, "::", &v6));  // initialize as zero
 
-    EXPECT_EQ(-ENETUNREACH, generateIpv6Address(tun.name().c_str(), v4, nat64Prefix, &v6));
+    // 0u is MARK_UNSET
+    EXPECT_EQ(-ENETUNREACH, generateIpv6Address(tun.name().c_str(), v4, nat64Prefix, &v6, 0u));
     EXPECT_TRUE(IN6_IS_ADDR_ULA(&v6));
 
     tun.destroy();
diff --git a/service/native/libs/libclat/include/libclat/clatutils.h b/service/native/libs/libclat/include/libclat/clatutils.h
index 812c86e..991b193 100644
--- a/service/native/libs/libclat/include/libclat/clatutils.h
+++ b/service/native/libs/libclat/include/libclat/clatutils.h
@@ -24,7 +24,7 @@
 in_addr_t selectIpv4Address(const in_addr ip, int16_t prefixlen);
 void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Prefix);
 int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix,
-                        in6_addr* v6);
+                        in6_addr* v6, uint32_t mark);
 int detect_mtu(const struct in6_addr* plat_subnet, uint32_t plat_suffix, uint32_t mark);
 int configure_packet_socket(int sock, in6_addr* addr, int ifindex);
 
diff --git a/service/proguard.flags b/service/proguard.flags
index f546e82..864a28b 100644
--- a/service/proguard.flags
+++ b/service/proguard.flags
@@ -1,15 +1,17 @@
-# Make sure proguard keeps all connectivity classes
-# TODO: instead of keeping everything, consider listing only "entry points"
-# (service loader, JNI registered methods, etc) and letting the optimizer do its job
--keep class android.net.** { *; }
--keep class !com.android.server.nearby.**,com.android.server.** { *; }
 
-# Prevent proguard from stripping out any nearby-service and fast-pair-lite-protos fields.
--keep class com.android.server.nearby.NearbyService { *; }
+# Keep JNI registered methods
+-keepclasseswithmembers,includedescriptorclasses class * { native <methods>; }
 
-# The lite proto runtime uses reflection to access fields based on the names in
-# the schema, keep all the fields.
-# This replicates the base proguard rule used by the build by default
-# (proguard_basic_keeps.flags), but needs to be specified here because the
-# com.google.protobuf package is jarjared to use a package prefix.
--keepclassmembers class * extends **.com.google.protobuf.MessageLite { <fields>; }
+# Keep classes extending structured message.
+-keepclassmembers public class * extends **.com.android.net.module.util.Struct {
+    *;
+}
+
+-keepclassmembers class com.android.server.**,android.net.**,com.android.networkstack.** {
+    static final % POLICY_*;
+    static final % NOTIFY_TYPE_*;
+    static final % TRANSPORT_*;
+    static final % CMD_*;
+    static final % EVENT_*;
+}
+
diff --git a/service/src/com/android/metrics/stats.proto b/service/src/com/android/metrics/stats.proto
new file mode 100644
index 0000000..48b8316
--- /dev/null
+++ b/service/src/com/android/metrics/stats.proto
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package com.android.metrics;
+
+import "frameworks/proto_logging/stats/enums/stats/connectivity/connectivity_service.proto";
+
+/**
+ * Logs NSD(Network service discovery) client session
+ *
+ * Log from:
+ *     packages/modules/Connectivity/service-t/src/com/android/server/NsdService
+ */
+message NetworkNsdReported {
+  // Indicate if the device is using the legacy or the new implementation
+  optional bool is_legacy = 1;
+
+  // It is a random number to represent different clients. Each client is an app on the device.
+  optional int32 client_id = 2;
+
+  // It is a increment_number to represent different transactions.
+  // Each transaction is a request from an app client.
+  optional int32 transaction_id = 3;
+
+  // Indicate the service in resolution is a known service in the discovered services cache
+  optional bool is_known_service = 4;
+
+  // Record each NSD session type
+  optional .android.stats.connectivity.NsdEventType type = 5;
+
+  // The process duration of the event in milli-second
+  optional int64 event_duration_millisec = 6;
+
+  // Record each mdns query result
+  optional .android.stats.connectivity.MdnsQueryResult query_result = 7;
+
+  // Count of services in cache at the end of discovery
+  optional int32 found_service_count = 8;
+
+  // Count of found callback when discovery is stopped
+  optional int32 found_callback_count = 9;
+
+  // Count of lost callback when discovery is stopped
+  optional int32 lost_callback_count = 10;
+
+  // Record query service count before unregistered service
+  optional int32 replied_requests_count = 11;
+}
+
+/**
+ * Logs the number of network count on each list of transports
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkCountPerTransports {
+    // the number of network count on each list of transports
+    repeated NetworkCountForTransports network_count_for_transports = 1;
+}
+
+/**
+ * Logs the number of network count and transport type
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkCountForTransports {
+    // Transport types of the network
+    optional int32 transport_types = 1;
+
+    // Number of networks for one list of transport types
+    optional int32 network_count = 2;
+}
+
+/**
+ * Logs a list of networks
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkList {
+    repeated NetworkDescription network_description = 1;
+}
+
+/**
+ * Logs connection duration in seconds and list of transports
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message ConnectionDurationForTransports {
+    // Transport types of the network
+    optional int32 transport_types = 1;
+
+    // Time duration that the device stays connected to the network
+    optional int32 duration_sec = 2;
+}
+
+/**
+ * Logs connection duration on each list of transports, in seconds
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message ConnectionDurationPerTransports {
+    repeated ConnectionDurationForTransports connection_duration_for_transports = 1;
+}
+
+/**
+ * Logs network request count & request type
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message RequestCountForType {
+    // The type of network request
+    optional .android.stats.connectivity.RequestType request_type = 1;
+
+    // Number of network requests
+    optional int32 request_count = 2;
+}
+
+/**
+ * Logs network request count
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkRequestCount {
+    // Network request count for request type
+    repeated RequestCountForType request_count_for_type = 1;
+}
+
+/**
+ * Logs information about a network
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkDescription {
+    // The transport types of the network. A network may include multiple transport types.
+    // Each transfer type is represented by a different bit, defined in
+    // packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java
+    optional int32 transport_types = 1;
+
+    // Indicates the network is metered, non-metered or temporarily-unmetered
+    optional .android.stats.connectivity.MeteredState metered_state = 2;
+
+    // Indicates the network is validated, non-validated, partial or portal
+    optional .android.stats.connectivity.ValidatedState validated_state = 3;
+
+    // Record the bitmask of all the policies applied to this score of network.
+    // Each policy is represented by a different bit, defined in
+    // packages/modules/Connectivity/service/src/com/android/server/connectivity/FullScore.java
+    optional int64 score_policies = 4;
+
+    // The capabilities of the network. A network may include multiple network capabilities.
+    // Each capability is represented by a different bit, defined in
+    // packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java
+    optional int64 capabilities = 5;
+
+    // Bitfield representing the network's enterprise capability identifier, defined in
+    // packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java
+    optional int32 enterprise_id = 6;
+}
+
+/**
+ * Pulls a list of NumberOfRematchesPerReason.
+ *
+ * Pulled from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NumberOfRematchesPerReason {
+    // Number of network rematches for each rematch reason
+    repeated NumberOfRematchesForReason number_of_rematches_per_reason= 1;
+}
+
+/**
+ * Logs number of network rematches for rematch reason
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NumberOfRematchesForReason {
+    // The reason of network rematch
+    optional .android.stats.connectivity.RematchReason rematch_reason = 1;
+
+    // Number of network rematches
+    optional int32 rematch_count = 2;
+};
+
+/**
+ * Pulls information for connectivity stats.
+ *
+ * Pulled from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message ConnectivityStateSample {
+    // Number of networks per list of transports
+    optional NetworkCountPerTransports network_count_per_transports = 1;
+
+    // This is a list of networks with their transports and the duration
+    optional ConnectionDurationPerTransports connection_duration_per_transports = 2;
+
+    // Number of requests per category
+    optional NetworkRequestCount network_request_count  = 3;
+
+    // Full list of network details (slice by transport / meteredness / internet+validated)
+    optional NetworkList networks = 4;
+}
+
+
+/**
+ * Pulls information for network selection rematch info.
+ *
+ * Pulled from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkSelectionRematchReasonsInfo {
+    // Number of rematch per rematch reason
+    optional NumberOfRematchesPerReason number_of_rematches_per_reason = 1;
+}
+
+/**
+ * Logs rematch information for the default network
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message DefaultNetworkRematchInfo {
+    // The session id comes from each reboot, this is used to correlate the statistics of the
+    // networkselect on the same boot
+    optional int64 session_id = 1;
+
+    // The information of old device default network
+    optional NetworkDescription old_network = 2;
+
+    // The information of new device default network
+    optional NetworkDescription new_network = 3;
+
+    // The reason of network rematch
+    optional .android.stats.connectivity.RematchReason rematch_reason = 4;
+
+    // The time duration the device kept the old network as the default in seconds
+    optional int32 time_duration_on_old_network_sec = 5;
+}
+
+/**
+ * Logs network selection performance
+ *
+ * Logs from:
+ *   packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
+ */
+message NetworkSelectionPerformance {
+    // Number of network requests
+    optional int32 number_of_network_requests = 1;
+
+    // List of networks right now
+    // (slice by transport / meteredness / internet+validated)
+    optional NetworkList networks = 2;
+
+    // The latency of selection computed in milli-second
+    optional int32 selection_computed_latency_milli = 3;
+
+    // The latency of selection applied in milli-second
+    optional int32 selection_applied_latency_milli = 4;
+
+    // The latency of selection issued in milli-second
+    optional int32 selection_issued_latency_milli = 5;
+}
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index dc5c4c7..b8a8fb4 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -27,12 +27,17 @@
 import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
 import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
 import static android.net.INetd.PERMISSION_INTERNET;
+import static android.net.INetd.PERMISSION_NONE;
 import static android.net.INetd.PERMISSION_UNINSTALLED;
+import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.ENODEV;
 import static android.system.OsConstants.ENOENT;
 import static android.system.OsConstants.EOPNOTSUPP;
 
+import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
+
+import android.app.StatsManager;
 import android.content.Context;
 import android.net.INetd;
 import android.os.RemoteException;
@@ -41,19 +46,31 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
+import android.util.Pair;
+import android.util.StatsEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
 import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfDump;
 import com.android.net.module.util.BpfMap;
 import com.android.net.module.util.DeviceConfigUtils;
 import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.Struct.U32;
 import com.android.net.module.util.Struct.U8;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
+import java.util.StringJoiner;
 
 /**
  * BpfNetMaps is responsible for providing traffic controller relevant functionality.
@@ -94,16 +111,19 @@
             "/sys/fs/bpf/netd_shared/map_netd_uid_owner_map";
     private static final String UID_PERMISSION_MAP_PATH =
             "/sys/fs/bpf/netd_shared/map_netd_uid_permission_map";
-    private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
-    private static final U32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new U32(1);
+    private static final String COOKIE_TAG_MAP_PATH =
+            "/sys/fs/bpf/netd_shared/map_netd_cookie_tag_map";
+    private static final S32 UID_RULES_CONFIGURATION_KEY = new S32(0);
+    private static final S32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new S32(1);
     private static final long UID_RULES_DEFAULT_CONFIGURATION = 0;
     private static final long STATS_SELECT_MAP_A = 0;
     private static final long STATS_SELECT_MAP_B = 1;
 
-    private static IBpfMap<U32, U32> sConfigurationMap = null;
+    private static IBpfMap<S32, U32> sConfigurationMap = null;
     // BpfMap for UID_OWNER_MAP_PATH. This map is not accessed by others.
-    private static IBpfMap<U32, UidOwnerValue> sUidOwnerMap = null;
-    private static IBpfMap<U32, U8> sUidPermissionMap = null;
+    private static IBpfMap<S32, UidOwnerValue> sUidOwnerMap = null;
+    private static IBpfMap<S32, U8> sUidPermissionMap = null;
+    private static IBpfMap<CookieTagMapKey, CookieTagMapValue> sCookieTagMap = null;
 
     // LINT.IfChange(match_type)
     @VisibleForTesting public static final long NO_MATCH = 0;
@@ -121,6 +141,25 @@
     @VisibleForTesting public static final long OEM_DENY_3_MATCH = (1 << 11);
     // LINT.ThenChange(packages/modules/Connectivity/bpf_progs/bpf_shared.h)
 
+    private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
+            Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
+            Pair.create(PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
+    );
+    private static final List<Pair<Long, String>> MATCH_LIST = Arrays.asList(
+            Pair.create(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH"),
+            Pair.create(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH"),
+            Pair.create(DOZABLE_MATCH, "DOZABLE_MATCH"),
+            Pair.create(STANDBY_MATCH, "STANDBY_MATCH"),
+            Pair.create(POWERSAVE_MATCH, "POWERSAVE_MATCH"),
+            Pair.create(RESTRICTED_MATCH, "RESTRICTED_MATCH"),
+            Pair.create(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH"),
+            Pair.create(IIF_MATCH, "IIF_MATCH"),
+            Pair.create(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH"),
+            Pair.create(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH"),
+            Pair.create(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH"),
+            Pair.create(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH")
+    );
+
     /**
      * Set sEnableJavaBpfMap for test.
      */
@@ -133,7 +172,7 @@
      * Set configurationMap for test.
      */
     @VisibleForTesting
-    public static void setConfigurationMapForTest(IBpfMap<U32, U32> configurationMap) {
+    public static void setConfigurationMapForTest(IBpfMap<S32, U32> configurationMap) {
         sConfigurationMap = configurationMap;
     }
 
@@ -141,7 +180,7 @@
      * Set uidOwnerMap for test.
      */
     @VisibleForTesting
-    public static void setUidOwnerMapForTest(IBpfMap<U32, UidOwnerValue> uidOwnerMap) {
+    public static void setUidOwnerMapForTest(IBpfMap<S32, UidOwnerValue> uidOwnerMap) {
         sUidOwnerMap = uidOwnerMap;
     }
 
@@ -149,37 +188,55 @@
      * Set uidPermissionMap for test.
      */
     @VisibleForTesting
-    public static void setUidPermissionMapForTest(IBpfMap<U32, U8> uidPermissionMap) {
+    public static void setUidPermissionMapForTest(IBpfMap<S32, U8> uidPermissionMap) {
         sUidPermissionMap = uidPermissionMap;
     }
 
-    private static IBpfMap<U32, U32> getConfigurationMap() {
+    /**
+     * Set cookieTagMap for test.
+     */
+    @VisibleForTesting
+    public static void setCookieTagMapForTest(
+            IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap) {
+        sCookieTagMap = cookieTagMap;
+    }
+
+    private static IBpfMap<S32, U32> getConfigurationMap() {
         try {
             return new BpfMap<>(
-                    CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, U32.class);
+                    CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U32.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open netd configuration map", e);
         }
     }
 
-    private static IBpfMap<U32, UidOwnerValue> getUidOwnerMap() {
+    private static IBpfMap<S32, UidOwnerValue> getUidOwnerMap() {
         try {
             return new BpfMap<>(
-                    UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, UidOwnerValue.class);
+                    UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, UidOwnerValue.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open uid owner map", e);
         }
     }
 
-    private static IBpfMap<U32, U8> getUidPermissionMap() {
+    private static IBpfMap<S32, U8> getUidPermissionMap() {
         try {
             return new BpfMap<>(
-                    UID_PERMISSION_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, U8.class);
+                    UID_PERMISSION_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U8.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open uid permission map", e);
         }
     }
 
+    private static IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() {
+        try {
+            return new BpfMap<>(COOKIE_TAG_MAP_PATH, BpfMap.BPF_F_RDWR,
+                    CookieTagMapKey.class, CookieTagMapValue.class);
+        } catch (ErrnoException e) {
+            throw new IllegalStateException("Cannot open cookie tag map", e);
+        }
+    }
+
     private static void initBpfMaps() {
         if (sConfigurationMap == null) {
             sConfigurationMap = getConfigurationMap();
@@ -209,6 +266,10 @@
         if (sUidPermissionMap == null) {
             sUidPermissionMap = getUidPermissionMap();
         }
+
+        if (sCookieTagMap == null) {
+            sCookieTagMap = getCookieTagMap();
+        }
     }
 
     /**
@@ -220,15 +281,19 @@
         if (sEnableJavaBpfMap == null) {
             sEnableJavaBpfMap = DeviceConfigUtils.isFeatureEnabled(context,
                     DeviceConfig.NAMESPACE_TETHERING, BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP,
-                    SdkLevel.isAtLeastU() /* defaultValue */);
+                    false /* defaultValue */) || SdkLevel.isAtLeastU();
         }
         Log.d(TAG, "BpfNetMaps is initialized with sEnableJavaBpfMap=" + sEnableJavaBpfMap);
 
         initBpfMaps();
-        native_init();
+        native_init(!sEnableJavaBpfMap /* startSkDestroyListener */);
         sInitialized = true;
     }
 
+    public boolean isSkDestroyListenerRunning() {
+        return !sEnableJavaBpfMap;
+    }
+
     /**
      * Dependencies of BpfNetMaps, for injection in tests.
      */
@@ -247,6 +312,22 @@
         public int synchronizeKernelRCU() {
             return native_synchronizeKernelRCU();
         }
+
+        /**
+         * Build Stats Event for NETWORK_BPF_MAP_INFO atom
+         */
+        public StatsEvent buildStatsEvent(final int cookieTagMapSize, final int uidOwnerMapSize,
+                final int uidPermissionMapSize) {
+            return ConnectivityStatsLog.buildStatsEvent(NETWORK_BPF_MAP_INFO, cookieTagMapSize,
+                    uidOwnerMapSize, uidPermissionMapSize);
+        }
+
+        /**
+         * Call native_dump
+         */
+        public void nativeDump(final FileDescriptor fd, final boolean verbose) {
+            native_dump(fd, verbose);
+        }
     }
 
     /** Constructor used after T that doesn't need to use netd anymore. */
@@ -335,7 +416,7 @@
     private void removeRule(final int uid, final long match, final String caller) {
         try {
             synchronized (sUidOwnerMap) {
-                final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new U32(uid));
+                final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new S32(uid));
 
                 if (oldMatch == null) {
                     throw new ServiceSpecificException(ENOENT,
@@ -348,9 +429,9 @@
                 );
 
                 if (newMatch.rule == 0) {
-                    sUidOwnerMap.deleteEntry(new U32(uid));
+                    sUidOwnerMap.deleteEntry(new S32(uid));
                 } else {
-                    sUidOwnerMap.updateEntry(new U32(uid), newMatch);
+                    sUidOwnerMap.updateEntry(new S32(uid), newMatch);
                 }
             }
         } catch (ErrnoException e) {
@@ -359,7 +440,7 @@
         }
     }
 
-    private void addRule(final int uid, final long match, final long iif, final String caller) {
+    private void addRule(final int uid, final long match, final int iif, final String caller) {
         if (match != IIF_MATCH && iif != 0) {
             throw new ServiceSpecificException(EINVAL,
                     "Non-interface match must have zero interface index");
@@ -367,7 +448,7 @@
 
         try {
             synchronized (sUidOwnerMap) {
-                final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new U32(uid));
+                final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new S32(uid));
 
                 final UidOwnerValue newMatch;
                 if (oldMatch != null) {
@@ -381,7 +462,7 @@
                             match
                     );
                 }
-                sUidOwnerMap.updateEntry(new U32(uid), newMatch);
+                sUidOwnerMap.updateEntry(new S32(uid), newMatch);
             }
         } catch (ErrnoException e) {
             throw new ServiceSpecificException(e.errno,
@@ -801,7 +882,7 @@
             if (permissions == PERMISSION_UNINSTALLED || permissions == PERMISSION_INTERNET) {
                 for (final int uid : uids) {
                     try {
-                        sUidPermissionMap.deleteEntry(new U32(uid));
+                        sUidPermissionMap.deleteEntry(new S32(uid));
                     } catch (ErrnoException e) {
                         Log.e(TAG, "Failed to remove uid " + uid + " from permission map: " + e);
                     }
@@ -811,7 +892,7 @@
 
             for (final int uid : uids) {
                 try {
-                    sUidPermissionMap.updateEntry(new U32(uid), new U8((short) permissions));
+                    sUidPermissionMap.updateEntry(new S32(uid), new U8((short) permissions));
                 } catch (ErrnoException e) {
                     Log.e(TAG, "Failed to set permission "
                             + permissions + " to uid " + uid + ": " + e);
@@ -822,24 +903,160 @@
         }
     }
 
+    /** Register callback for statsd to pull atom. */
+    public void setPullAtomCallback(final Context context) {
+        throwIfPreT("setPullAtomCallback is not available on pre-T devices");
+
+        final StatsManager statsManager = context.getSystemService(StatsManager.class);
+        statsManager.setPullAtomCallback(NETWORK_BPF_MAP_INFO, null /* metadata */,
+                BackgroundThread.getExecutor(), this::pullBpfMapInfoAtom);
+    }
+
+    private <K extends Struct, V extends Struct> int getMapSize(IBpfMap<K, V> map)
+            throws ErrnoException {
+        // forEach could restart iteration from the beginning if there is a concurrent entry
+        // deletion. netd and skDestroyListener could delete CookieTagMap entry concurrently.
+        // So using Set to count the number of entry in the map.
+        Set<K> keySet = new ArraySet<>();
+        map.forEach((k, v) -> keySet.add(k));
+        return keySet.size();
+    }
+
+    /** Callback for StatsManager#setPullAtomCallback */
+    @VisibleForTesting
+    public int pullBpfMapInfoAtom(final int atomTag, final List<StatsEvent> data) {
+        if (atomTag != NETWORK_BPF_MAP_INFO) {
+            Log.e(TAG, "Unexpected atom tag: " + atomTag);
+            return StatsManager.PULL_SKIP;
+        }
+
+        try {
+            data.add(mDeps.buildStatsEvent(getMapSize(sCookieTagMap), getMapSize(sUidOwnerMap),
+                    getMapSize(sUidPermissionMap)));
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to pull NETWORK_BPF_MAP_INFO atom: " + e);
+            return StatsManager.PULL_SKIP;
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private String permissionToString(int permissionMask) {
+        if (permissionMask == PERMISSION_NONE) {
+            return "PERMISSION_NONE";
+        }
+        if (permissionMask == PERMISSION_UNINSTALLED) {
+            // PERMISSION_UNINSTALLED should never appear in the map
+            return "PERMISSION_UNINSTALLED error!";
+        }
+
+        final StringJoiner sj = new StringJoiner(" ");
+        for (Pair<Integer, String> permission: PERMISSION_LIST) {
+            final int permissionFlag = permission.first;
+            final String permissionName = permission.second;
+            if ((permissionMask & permissionFlag) != 0) {
+                sj.add(permissionName);
+                permissionMask &= ~permissionFlag;
+            }
+        }
+        if (permissionMask != 0) {
+            sj.add("PERMISSION_UNKNOWN(" + permissionMask + ")");
+        }
+        return sj.toString();
+    }
+
+    private String matchToString(long matchMask) {
+        if (matchMask == NO_MATCH) {
+            return "NO_MATCH";
+        }
+
+        final StringJoiner sj = new StringJoiner(" ");
+        for (Pair<Long, String> match: MATCH_LIST) {
+            final long matchFlag = match.first;
+            final String matchName = match.second;
+            if ((matchMask & matchFlag) != 0) {
+                sj.add(matchName);
+                matchMask &= ~matchFlag;
+            }
+        }
+        if (matchMask != 0) {
+            sj.add("UNKNOWN_MATCH(" + matchMask + ")");
+        }
+        return sj.toString();
+    }
+
+    private void dumpOwnerMatchConfig(final IndentingPrintWriter pw) {
+        try {
+            final long match = sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val;
+            pw.println("current ownerMatch configuration: " + match + " " + matchToString(match));
+        } catch (ErrnoException e) {
+            pw.println("Failed to read ownerMatch configuration: " + e);
+        }
+    }
+
+    private void dumpCurrentStatsMapConfig(final IndentingPrintWriter pw) {
+        try {
+            final long config = sConfigurationMap.getValue(CURRENT_STATS_MAP_CONFIGURATION_KEY).val;
+            final String currentStatsMap =
+                    (config == STATS_SELECT_MAP_A) ? "SELECT_MAP_A" : "SELECT_MAP_B";
+            pw.println("current statsMap configuration: " + config + " " + currentStatsMap);
+        } catch (ErrnoException e) {
+            pw.println("Falied to read current statsMap configuration: " + e);
+        }
+    }
+
     /**
      * Dump BPF maps
      *
+     * @param pw print writer
      * @param fd file descriptor to output
+     * @param verbose verbose dump flag, if true dump the BpfMap contents
      * @throws IOException when file descriptor is invalid.
      * @throws ServiceSpecificException when the method is called on an unsupported device.
      */
-    public void dump(final FileDescriptor fd, boolean verbose)
+    public void dump(final IndentingPrintWriter pw, final FileDescriptor fd, boolean verbose)
             throws IOException, ServiceSpecificException {
         if (PRE_T) {
             throw new ServiceSpecificException(
                     EOPNOTSUPP, "dumpsys connectivity trafficcontroller dump not available on pre-T"
                     + " devices, use dumpsys netd trafficcontroller instead.");
         }
-        native_dump(fd, verbose);
+        mDeps.nativeDump(fd, verbose);
+
+        pw.println();
+        pw.println("sEnableJavaBpfMap: " + sEnableJavaBpfMap);
+        if (verbose) {
+            pw.println();
+            pw.println("BPF map content:");
+            pw.increaseIndent();
+
+            dumpOwnerMatchConfig(pw);
+            dumpCurrentStatsMapConfig(pw);
+            pw.println();
+
+            // TODO: Remove CookieTagMap content dump
+            // NetworkStatsService also dumps CookieTagMap and NetworkStatsService is a right place
+            // to dump CookieTagMap. But the TagSocketTest in CTS depends on this dump so the tests
+            // need to be updated before remove the dump from BpfNetMaps.
+            BpfDump.dumpMap(sCookieTagMap, pw, "sCookieTagMap",
+                    (key, value) -> "cookie=" + key.socketCookie
+                            + " tag=0x" + Long.toHexString(value.tag)
+                            + " uid=" + value.uid);
+            BpfDump.dumpMap(sUidOwnerMap, pw, "sUidOwnerMap",
+                    (uid, match) -> {
+                        if ((match.rule & IIF_MATCH) != 0) {
+                            // TODO: convert interface index to interface name by IfaceIndexNameMap
+                            return uid.val + " " + matchToString(match.rule) + " " + match.iif;
+                        } else {
+                            return uid.val + " " + matchToString(match.rule);
+                        }
+                    });
+            BpfDump.dumpMap(sUidPermissionMap, pw, "sUidPermissionMap",
+                    (uid, permission) -> uid.val + " " + permissionToString(permission.val));
+            pw.decreaseIndent();
+        }
     }
 
-    private static native void native_init();
+    private static native void native_init(boolean startSkDestroyListener);
     private native int native_addNaughtyApp(int uid);
     private native int native_removeNaughtyApp(int uid);
     private native int native_addNiceApp(int uid);
@@ -852,6 +1069,6 @@
     private native int native_updateUidLockdownRule(int uid, boolean add);
     private native int native_swapActiveStatsMap();
     private native void native_setPermissionForUids(int permissions, int[] uids);
-    private native void native_dump(FileDescriptor fd, boolean verbose);
+    private static native void native_dump(FileDescriptor fd, boolean verbose);
     private static native int native_synchronizeKernelRCU();
 }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index afb3ca2..4c9e3a3 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -205,12 +205,12 @@
 import android.net.resolv.aidl.Nat64PrefixEventParcel;
 import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
 import android.net.shared.PrivateDnsConfig;
-import android.net.util.MultinetworkPolicyTracker;
 import android.net.wifi.WifiInfo;
 import android.os.BatteryStatsManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -250,13 +250,14 @@
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.BitUtils;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.DeviceConfigUtils;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.net.module.util.LocationPermissionChecker;
-import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PerUidCounter;
 import com.android.net.module.util.PermissionUtils;
 import com.android.net.module.util.TcUtils;
@@ -272,6 +273,7 @@
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.MultinetworkPolicyTracker;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkNotificationManager;
@@ -353,13 +355,22 @@
     // connect anyway?" dialog after the user selects a network that doesn't validate.
     private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
 
+    // How long to wait before considering that a network is bad in the absence of any form
+    // of connectivity (valid, partial, captive portal). If none has been detected after this
+    // delay, the stack considers this network bad, which may affect how it's handled in ranking
+    // according to config_networkAvoidBadWifi.
+    // Timeout in case the "actively prefer bad wifi" feature is on
+    private static final int ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS = 20 * 1000;
+    // Timeout in case the "actively prefer bad wifi" feature is off
+    private static final int DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS = 8 * 1000;
+
     // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
     private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
 
     // The maximum value for the blocking validation result, in milliseconds.
-    public static final int MAX_VALIDATION_FAILURE_BLOCKING_TIME_MS = 10000;
+    public static final int MAX_VALIDATION_IGNORE_AFTER_ROAM_TIME_MS = 10000;
 
     // The maximum number of network request allowed per uid before an exception is thrown.
     @VisibleForTesting
@@ -581,12 +592,6 @@
     private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
 
     /**
-     * used to ask the user to confirm a connection to an unvalidated network.
-     * obj  = network
-     */
-    private static final int EVENT_PROMPT_UNVALIDATED = 29;
-
-    /**
      * used internally to (re)configure always-on networks.
      */
     private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30;
@@ -725,6 +730,14 @@
     private static final int EVENT_INGRESS_RATE_LIMIT_CHANGED = 56;
 
     /**
+     * The initial evaluation period is over for this network.
+     *
+     * If no form of connectivity has been found on this network (valid, partial, captive portal)
+     * then the stack will now consider it to have been determined bad.
+     */
+    private static final int EVENT_INITIAL_EVALUATION_TIMEOUT = 57;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -775,7 +788,8 @@
     final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
 
     private final DnsManager mDnsManager;
-    private final NetworkRanker mNetworkRanker;
+    @VisibleForTesting
+    final NetworkRanker mNetworkRanker;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
@@ -1409,7 +1423,6 @@
                 new RequestInfoPerUidCounter(MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1);
 
         mMetricsLog = logger;
-        mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultRequest();
         mDefaultRequest = new NetworkRequestInfo(
                 Process.myUid(), defaultInternetRequest, null,
@@ -1438,7 +1451,11 @@
         mCellularRadioTimesharingCapable =
                 mResources.get().getBoolean(R.bool.config_cellular_radio_timesharing_capable);
 
+        mNetd = netd;
+        mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
         mHandlerThread = mDeps.makeHandlerThread();
+        mPermissionMonitor =
+                new PermissionMonitor(mContext, mNetd, mBpfNetMaps, mHandlerThread);
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
         mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
@@ -1453,8 +1470,6 @@
         mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
         mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
-        mNetd = netd;
-        mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
@@ -1484,8 +1499,6 @@
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
-        mPermissionMonitor = new PermissionMonitor(mContext, mNetd, mBpfNetMaps);
-
         mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
         // Listen for user add/removes to inform PermissionMonitor.
         // Should run on mHandler to avoid any races.
@@ -1530,6 +1543,9 @@
 
         mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
                 mContext, mHandler, () -> updateAvoidBadWifi());
+        mNetworkRanker =
+                new NetworkRanker(new NetworkRanker.Configuration(activelyPreferBadWifi()));
+
         mMultinetworkPolicyTracker.start();
 
         mDnsManager = new DnsManager(mContext, mDnsResolver);
@@ -2792,6 +2808,13 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
+    private void enforceSettingsOrUseRestrictedNetworksPermission() {
+        enforceAnyPermissionOf(mContext,
+                android.Manifest.permission.NETWORK_SETTINGS,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+    }
+
     private void enforceNetworkFactoryPermission() {
         // TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
         if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
@@ -3018,7 +3041,11 @@
         // Calling PermissionMonitor#startMonitoring() in systemReadyInternal() and the
         // MultipathPolicyTracker.start() is called in NetworkPolicyManagerService#systemReady()
         // to ensure the tracking will be initialized correctly.
-        mPermissionMonitor.startMonitoring();
+        final ConditionVariable startMonitoringDone = new ConditionVariable();
+        mHandler.post(() -> {
+            mPermissionMonitor.startMonitoring();
+            startMonitoringDone.open();
+        });
         mProxyTracker.loadGlobalProxy();
         registerDnsResolverUnsolicitedEventListener();
 
@@ -3040,6 +3067,16 @@
         if (!ConnectivitySettingsManager.getMobileDataPreferredUids(mContext).isEmpty()) {
             updateMobileDataPreferredUids();
         }
+
+        // On T+ devices, register callback for statsd to pull NETWORK_BPF_MAP_INFO atom
+        if (SdkLevel.isAtLeastT()) {
+            mBpfNetMaps.setPullAtomCallback(mContext);
+        }
+        // Wait PermissionMonitor to finish the permission update. Then MultipathPolicyTracker won't
+        // have permission problem. While CV#block() is unbounded in time and can in principle block
+        // forever, this replaces a synchronous call to PermissionMonitor#startMonitoring, which
+        // could have blocked forever too.
+        startMonitoringDone.block();
     }
 
     /**
@@ -3070,7 +3107,7 @@
      * Reads the network specific MTU size from resources.
      * and set it on it's iface.
      */
-    private void updateMtu(LinkProperties newLp, LinkProperties oldLp) {
+    private void updateMtu(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp) {
         final String iface = newLp.getInterfaceName();
         final int mtu = newLp.getMtu();
         if (oldLp == null && mtu == 0) {
@@ -3103,7 +3140,7 @@
     @VisibleForTesting
     protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
 
-    private void updateTcpBufferSizes(String tcpBufferSizes) {
+    private void updateTcpBufferSizes(@Nullable String tcpBufferSizes) {
         String[] values = null;
         if (tcpBufferSizes != null) {
             values = tcpBufferSizes.split(",");
@@ -3337,17 +3374,6 @@
         pw.increaseIndent();
         mNetworkActivityTracker.dump(pw);
         pw.decreaseIndent();
-
-        // pre-T is logged by netd.
-        if (SdkLevel.isAtLeastT()) {
-            pw.println();
-            pw.println("BPF programs & maps:");
-            pw.increaseIndent();
-            // Flush is required. Otherwise, the traces in fd can interleave with traces in pw.
-            pw.flush();
-            dumpTrafficController(pw, fd, /*verbose=*/ true);
-            pw.decreaseIndent();
-        }
     }
 
     private void dumpNetworks(IndentingPrintWriter pw) {
@@ -3449,7 +3475,7 @@
     private void dumpTrafficController(IndentingPrintWriter pw, final FileDescriptor fd,
             boolean verbose) {
         try {
-            mBpfNetMaps.dump(fd, verbose);
+            mBpfNetMaps.dump(pw, fd, verbose);
         } catch (ServiceSpecificException e) {
             pw.println(e.getMessage());
         } catch (IOException e) {
@@ -3639,8 +3665,18 @@
                     break;
                 }
                 case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: {
-                    // If nai is not yet created, or is already destroyed, ignore.
-                    if (!shouldDestroyNativeNetwork(nai)) break;
+                    if (!nai.isCreated()) {
+                        Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString()
+                                + ", tearing down instead");
+                        teardownUnneededNetwork(nai);
+                        break;
+                    }
+
+                    if (nai.isDestroyed()) {
+                        Log.d(TAG, "unregisterAfterReplacement on destroyed " + nai.toShortString()
+                                + ", ignoring");
+                        break;
+                    }
 
                     final int timeoutMs = (int) arg.second;
                     if (timeoutMs < 0 || timeoutMs > NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
@@ -3740,17 +3776,6 @@
                 case EVENT_PROVISIONING_NOTIFICATION: {
                     final boolean visible = toBool(msg.arg1);
                     // If captive portal status has changed, update capabilities or disconnect.
-                    if (nai != null && (visible != nai.captivePortalDetected())) {
-                        nai.setCaptivePortalDetected(visible);
-                        if (visible && ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID
-                                == getCaptivePortalMode()) {
-                            if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
-                            nai.onPreventAutomaticReconnect();
-                            teardownUnneededNetwork(nai);
-                            break;
-                        }
-                        updateCapabilitiesForNetwork(nai);
-                    }
                     if (!visible) {
                         // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
                         // notifications belong to the same network may be cleared unexpectedly.
@@ -3786,7 +3811,22 @@
 
         private void handleNetworkTested(
                 @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
-            final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+            final boolean valid = (testResult & NETWORK_VALIDATION_RESULT_VALID) != 0;
+            final boolean partial = (testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0;
+            final boolean portal = !TextUtils.isEmpty(redirectUrl);
+
+            // If there is any kind of working networking, then the NAI has been evaluated
+            // once. {@see NetworkAgentInfo#setEvaluated}, which returns whether this is
+            // the first time this ever happened.
+            final boolean someConnectivity = (valid || partial || portal);
+            final boolean becameEvaluated = someConnectivity && nai.setEvaluated();
+            // Because of b/245893397, if the score is updated when updateCapabilities is called,
+            // any callback that receives onAvailable for that rematch receives an extra caps
+            // callback. To prevent that, update the score in the agent so the updates below won't
+            // see an update to both caps and score at the same time.
+            // TODO : fix b/245893397 and remove this.
+            if (becameEvaluated) nai.updateScoreForNetworkAgentUpdate();
+
             if (!valid && shouldIgnoreValidationFailureAfterRoam(nai)) {
                 // Assume the validation failure is due to a temporary failure after roaming
                 // and ignore it. NetworkMonitor will continue to retry validation. If it
@@ -3797,8 +3837,12 @@
 
             final boolean wasValidated = nai.isValidated();
             final boolean wasPartial = nai.partialConnectivity();
-            nai.setPartialConnectivity((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
-            final boolean partialConnectivityChanged = (wasPartial != nai.partialConnectivity());
+            final boolean wasPortal = nai.captivePortalDetected();
+            nai.setPartialConnectivity(partial);
+            nai.setCaptivePortalDetected(portal);
+            nai.updateScoreForNetworkAgentUpdate();
+            final boolean partialConnectivityChanged = (wasPartial != partial);
+            final boolean portalChanged = (wasPortal != portal);
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -3806,7 +3850,7 @@
                         : "";
                 log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
-            if (valid != nai.isValidated()) {
+            if (valid != wasValidated) {
                 final FullScore oldScore = nai.getScore();
                 nai.setValidated(valid);
                 updateCapabilities(oldScore, nai, nai.networkCapabilities);
@@ -3829,8 +3873,23 @@
                 }
             } else if (partialConnectivityChanged) {
                 updateCapabilitiesForNetwork(nai);
+            } else if (portalChanged) {
+                if (portal && ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID
+                        == getCaptivePortalMode()) {
+                    if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
+                    nai.onPreventAutomaticReconnect();
+                    teardownUnneededNetwork(nai);
+                    return;
+                } else {
+                    updateCapabilitiesForNetwork(nai);
+                }
+            } else if (becameEvaluated) {
+                // If valid or partial connectivity changed, updateCapabilities* has
+                // done the rematch.
+                rematchAllNetworksAndRequests();
             }
             updateInetCondition(nai);
+
             // Let the NetworkAgent know the state of its network
             // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
             nai.onValidationStatusChanged(
@@ -3838,13 +3897,13 @@
                     redirectUrl);
 
             // If NetworkMonitor detects partial connectivity before
-            // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+            // EVENT_INITIAL_EVALUATION_TIMEOUT arrives, show the partial connectivity notification
             // immediately. Re-notify partial connectivity silently if no internet
             // notification already there.
             if (!wasPartial && nai.partialConnectivity()) {
                 // Remove delayed message if there is a pending message.
-                mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
-                handlePromptUnvalidated(nai.network);
+                mHandler.removeMessages(EVENT_INITIAL_EVALUATION_TIMEOUT, nai.network);
+                handleInitialEvaluationTimeout(nai.network);
             }
 
             if (wasValidated && !nai.isValidated()) {
@@ -4197,12 +4256,18 @@
         return nai.isCreated() && !nai.isDestroyed();
     }
 
-    private boolean shouldIgnoreValidationFailureAfterRoam(NetworkAgentInfo nai) {
+    @VisibleForTesting
+    boolean shouldIgnoreValidationFailureAfterRoam(NetworkAgentInfo nai) {
         // T+ devices should use unregisterAfterReplacement.
         if (SdkLevel.isAtLeastT()) return false;
+
+        // If the network never roamed, return false. The check below is not sufficient if time
+        // since boot is less than blockTimeOut, though that's extremely unlikely to happen.
+        if (nai.lastRoamTime == 0) return false;
+
         final long blockTimeOut = Long.valueOf(mResources.get().getInteger(
                 R.integer.config_validationFailureAfterRoamIgnoreTimeMillis));
-        if (blockTimeOut <= MAX_VALIDATION_FAILURE_BLOCKING_TIME_MS
+        if (blockTimeOut <= MAX_VALIDATION_IGNORE_AFTER_ROAM_TIME_MS
                 && blockTimeOut >= 0) {
             final long currentTimeMs = SystemClock.elapsedRealtime();
             long timeSinceLastRoam = currentTimeMs - nai.lastRoamTime;
@@ -4579,7 +4644,7 @@
             if (req.isListen() || req.isListenForBest()) {
                 continue;
             }
-            // If this Network is already the highest scoring Network for a request, or if
+            // If this Network is already the best Network for a request, or if
             // there is hope for it to become one if it validated, then it is needed.
             if (candidate.satisfies(req)) {
                 // As soon as a network is found that satisfies a request, return. Specifically for
@@ -4944,11 +5009,11 @@
         }
     }
 
-    private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
-        if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
+    /** Schedule evaluation timeout */
+    @VisibleForTesting
+    public void scheduleEvaluationTimeout(@NonNull final Network network, final long delayMs) {
         mHandler.sendMessageDelayed(
-                mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
-                PROMPT_UNVALIDATED_DELAY_MS);
+                mHandler.obtainMessage(EVENT_INITIAL_EVALUATION_TIMEOUT, network), delayMs);
     }
 
     @Override
@@ -5033,6 +5098,10 @@
         return mMultinetworkPolicyTracker.getAvoidBadWifi();
     }
 
+    private boolean activelyPreferBadWifi() {
+        return mMultinetworkPolicyTracker.getActivelyPreferBadWifi();
+    }
+
     /**
      * Return whether the device should maintain continuous, working connectivity by switching away
      * from WiFi networks having no connectivity.
@@ -5048,14 +5117,21 @@
     private void updateAvoidBadWifi() {
         ensureRunningOnConnectivityServiceThread();
         // Agent info scores and offer scores depend on whether cells yields to bad wifi.
+        final boolean avoidBadWifi = avoidBadWifi();
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             nai.updateScoreForNetworkAgentUpdate();
+            if (avoidBadWifi) {
+                // If the device is now avoiding bad wifi, remove notifications that might have
+                // been put up when the device didn't.
+                mNotifier.clearNotification(nai.network.getNetId(), NotificationType.LOST_INTERNET);
+            }
         }
         // UpdateOfferScore will update mNetworkOffers inline, so make a copy first.
         final ArrayList<NetworkOfferInfo> offersToUpdate = new ArrayList<>(mNetworkOffers);
         for (final NetworkOfferInfo noi : offersToUpdate) {
             updateOfferScore(noi.offer);
         }
+        mNetworkRanker.setConfiguration(new NetworkRanker.Configuration(activelyPreferBadWifi()));
         rematchAllNetworksAndRequests();
     }
 
@@ -5070,21 +5146,34 @@
 
         pw.println("Bad Wi-Fi avoidance: " + avoidBadWifi());
         pw.increaseIndent();
-        pw.println("Config restrict:   " + configRestrict);
+        pw.println("Config restrict:               " + configRestrict);
+        pw.println("Actively prefer bad wifi:      " + activelyPreferBadWifi());
 
-        final String value = mMultinetworkPolicyTracker.getAvoidBadWifiSetting();
+        final String settingValue = mMultinetworkPolicyTracker.getAvoidBadWifiSetting();
         String description;
         // Can't use a switch statement because strings are legal case labels, but null is not.
-        if ("0".equals(value)) {
+        if ("0".equals(settingValue)) {
             description = "get stuck";
-        } else if (value == null) {
+        } else if (settingValue == null) {
             description = "prompt";
-        } else if ("1".equals(value)) {
+        } else if ("1".equals(settingValue)) {
             description = "avoid";
         } else {
-            description = value + " (?)";
+            description = settingValue + " (?)";
         }
-        pw.println("User setting:      " + description);
+        pw.println("Avoid bad wifi setting:        " + description);
+
+        final Boolean configValue = BinderUtils.withCleanCallingIdentity(
+                () -> mMultinetworkPolicyTracker.deviceConfigActivelyPreferBadWifi());
+        if (null == configValue) {
+            description = "unset";
+        } else if (configValue) {
+            description = "force true";
+        } else {
+            description = "force false";
+        }
+        pw.println("Actively prefer bad wifi conf: " + description);
+        pw.println();
         pw.println("Network overrides:");
         pw.increaseIndent();
         for (NetworkAgentInfo nai : networksSortedById()) {
@@ -5183,23 +5272,38 @@
         return false;
     }
 
-    private void handlePromptUnvalidated(Network network) {
-        if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
-        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+    private void handleInitialEvaluationTimeout(@NonNull final Network network) {
+        if (VDBG || DDBG) log("handleInitialEvaluationTimeout " + network);
 
-        if (nai == null || !shouldPromptUnvalidated(nai)) {
-            return;
+        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (null == nai) return;
+
+        if (nai.setEvaluated()) {
+            // If setEvaluated() returned true, the network never had any form of connectivity.
+            // This may have an impact on request matching if bad WiFi avoidance is off and the
+            // network was found not to have Internet access.
+            nai.updateScoreForNetworkAgentUpdate();
+            rematchAllNetworksAndRequests();
+
+            // Also, if this is WiFi and it should be preferred actively, now is the time to
+            // prompt the user that they walked past and connected to a bad WiFi.
+            if (nai.networkCapabilities.hasTransport(TRANSPORT_WIFI)
+                    && !avoidBadWifi()
+                    && activelyPreferBadWifi()) {
+                // The notification will be removed if the network validates or disconnects.
+                showNetworkNotification(nai, NotificationType.LOST_INTERNET);
+                return;
+            }
         }
 
+        if (!shouldPromptUnvalidated(nai)) return;
+
         // Stop automatically reconnecting to this network in the future. Automatically connecting
         // to a network that provides no or limited connectivity is not useful, because the user
         // cannot use that network except through the notification shown by this method, and the
         // notification is only shown if the network is explicitly selected by the user.
         nai.onPreventAutomaticReconnect();
 
-        // TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
-        // NetworkMonitor detects the network is partial connectivity. Need to change the design to
-        // popup the notification immediately when the network is partial connectivity.
         if (nai.partialConnectivity()) {
             showNetworkNotification(nai, NotificationType.PARTIAL_CONNECTIVITY);
         } else {
@@ -5338,8 +5442,8 @@
                     handleSetAvoidUnvalidated((Network) msg.obj);
                     break;
                 }
-                case EVENT_PROMPT_UNVALIDATED: {
-                    handlePromptUnvalidated((Network) msg.obj);
+                case EVENT_INITIAL_EVALUATION_TIMEOUT: {
+                    handleInitialEvaluationTimeout((Network) msg.obj);
                     break;
                 }
                 case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: {
@@ -5675,7 +5779,7 @@
         return mProxyTracker.getGlobalProxy();
     }
 
-    private void handleApplyDefaultProxy(ProxyInfo proxy) {
+    private void handleApplyDefaultProxy(@Nullable ProxyInfo proxy) {
         if (proxy != null && TextUtils.isEmpty(proxy.getHost())
                 && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             proxy = null;
@@ -5687,8 +5791,8 @@
     // when any network changes proxy.
     // TODO: Remove usage of broadcast extras as they are deprecated and not applicable in a
     // multi-network world where an app might be bound to a non-default network.
-    private void updateProxy(LinkProperties newLp, LinkProperties oldLp) {
-        ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
+    private void updateProxy(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp) {
+        ProxyInfo newProxyInfo = newLp.getHttpProxy();
         ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
 
         if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
@@ -6014,14 +6118,12 @@
     }
 
     private void onUserAdded(@NonNull final UserHandle user) {
-        mPermissionMonitor.onUserAdded(user);
         if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
             handleSetOemNetworkPreference(mOemNetworkPreferences, null);
         }
     }
 
     private void onUserRemoved(@NonNull final UserHandle user) {
-        mPermissionMonitor.onUserRemoved(user);
         // If there was a network preference for this user, remove it.
         handleSetProfileNetworkPreference(
                 List.of(new ProfileNetworkPreferenceList.Preference(user, null, true)),
@@ -6570,7 +6672,7 @@
                 enforceAccessPermission();
                 break;
             case TRACK_SYSTEM_DEFAULT:
-                enforceSettingsPermission();
+                enforceSettingsOrUseRestrictedNetworksPermission();
                 networkCapabilities = new NetworkCapabilities(defaultNc);
                 break;
             case BACKGROUND_REQUEST:
@@ -7413,7 +7515,7 @@
     }
 
     private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
-            @NonNull LinkProperties oldLp) {
+            @Nullable LinkProperties oldLp) {
         int netId = networkAgent.network.getNetId();
 
         // The NetworkAgent does not know whether clatd is running on its network or not, or whether
@@ -7465,7 +7567,13 @@
             }
             // Start or stop DNS64 detection and 464xlat according to network state.
             networkAgent.clatd.update();
-            notifyIfacesChangedForNetworkStats();
+            // Notify NSS when relevant events happened. Currently, NSS only cares about
+            // interface changed to update clat interfaces accounting.
+            final boolean interfacesChanged = oldLp == null
+                    || !Objects.equals(newLp.getAllInterfaceNames(), oldLp.getAllInterfaceNames());
+            if (interfacesChanged) {
+                notifyIfacesChangedForNetworkStats();
+            }
             networkAgent.networkMonitor().notifyLinkPropertiesChanged(
                     new LinkProperties(newLp, true /* parcelSensitiveFields */));
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
@@ -7552,12 +7660,11 @@
 
     }
 
-    private void updateInterfaces(final @Nullable LinkProperties newLp,
+    private void updateInterfaces(final @NonNull LinkProperties newLp,
             final @Nullable LinkProperties oldLp, final int netId,
             final @NonNull NetworkCapabilities caps) {
         final CompareResult<String> interfaceDiff = new CompareResult<>(
-                oldLp != null ? oldLp.getAllInterfaceNames() : null,
-                newLp != null ? newLp.getAllInterfaceNames() : null);
+                oldLp != null ? oldLp.getAllInterfaceNames() : null, newLp.getAllInterfaceNames());
         if (!interfaceDiff.added.isEmpty()) {
             for (final String iface : interfaceDiff.added) {
                 try {
@@ -7618,12 +7725,13 @@
      * Have netd update routes from oldLp to newLp.
      * @return true if routes changed between oldLp and newLp
      */
-    private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
+    private boolean updateRoutes(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp,
+            int netId) {
         // compare the route diff to determine which routes have been updated
         final CompareOrUpdateResult<RouteInfo.RouteKey, RouteInfo> routeDiff =
                 new CompareOrUpdateResult<>(
                         oldLp != null ? oldLp.getAllRoutes() : null,
-                        newLp != null ? newLp.getAllRoutes() : null,
+                        newLp.getAllRoutes(),
                         (r) -> r.getRouteKey());
 
         // add routes before removing old in case it helps with continuous connectivity
@@ -7673,7 +7781,8 @@
                 || !routeDiff.updated.isEmpty();
     }
 
-    private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
+    private void updateDnses(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp,
+            int netId) {
         if (oldLp != null && newLp.isIdenticalDnses(oldLp)) {
             return;  // no updating necessary
         }
@@ -7690,8 +7799,8 @@
         }
     }
 
-    private void updateVpnFiltering(LinkProperties newLp, LinkProperties oldLp,
-            NetworkAgentInfo nai) {
+    private void updateVpnFiltering(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp,
+            @NonNull NetworkAgentInfo nai) {
         final String oldIface = getVpnIsolationInterface(nai, nai.networkCapabilities, oldLp);
         final String newIface = getVpnIsolationInterface(nai, nai.networkCapabilities, newLp);
         final boolean wasFiltering = requiresVpnAllowRule(nai, oldLp, oldIface);
@@ -7762,7 +7871,7 @@
             @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
         underlyingNetworks = underlyingNetworksOrDefault(
                 agentCaps.getOwnerUid(), underlyingNetworks);
-        long transportTypes = NetworkCapabilitiesUtils.packBits(agentCaps.getTransportTypes());
+        long transportTypes = BitUtils.packBits(agentCaps.getTransportTypes());
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         // metered if any underlying is metered, or originally declared metered by the agent.
@@ -7815,7 +7924,7 @@
             suspended = false;
         }
 
-        newNc.setTransportTypes(NetworkCapabilitiesUtils.unpackBits(transportTypes));
+        newNc.setTransportTypes(BitUtils.unpackBits(transportTypes));
         newNc.setLinkDownstreamBandwidthKbps(downKbps);
         newNc.setLinkUpstreamBandwidthKbps(upKbps);
         newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
@@ -7927,6 +8036,10 @@
             @NonNull final NetworkCapabilities nc) {
         NetworkCapabilities newNc = mixInCapabilities(nai, nc);
         if (Objects.equals(nai.networkCapabilities, newNc)) return;
+        final String differences = newNc.describeCapsDifferencesFrom(nai.networkCapabilities);
+        if (null != differences) {
+            Log.i(TAG, "Update capabilities for net " + nai.network + " : " + differences);
+        }
         updateNetworkPermissions(nai, newNc);
         final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
 
@@ -8224,7 +8337,8 @@
         }
     }
 
-    public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+    public void handleUpdateLinkProperties(@NonNull NetworkAgentInfo nai,
+            @NonNull LinkProperties newLp) {
         ensureRunningOnConnectivityServiceThread();
 
         if (!mNetworkAgentInfos.contains(nai)) {
@@ -9203,7 +9317,10 @@
                 networkAgent.networkMonitor().notifyNetworkConnected(params.linkProperties,
                         params.networkCapabilities);
             }
-            scheduleUnvalidatedPrompt(networkAgent);
+            final long delay = activelyPreferBadWifi()
+                    ? ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS
+                    : DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS;
+            scheduleEvaluationTimeout(networkAgent.network, delay);
 
             // Whether a particular NetworkRequest listen should cause signal strength thresholds to
             // be communicated to a particular NetworkAgent depends only on the network's immutable,
@@ -9617,6 +9734,8 @@
             try {
                 switch (cmd) {
                     case "airplane-mode":
+                        // Usage : adb shell cmd connectivity airplane-mode [enable|disable]
+                        // If no argument, get and display the current status
                         final String action = getNextArg();
                         if ("enable".equals(action)) {
                             setAirplaneMode(true);
@@ -9634,6 +9753,27 @@
                             onHelp();
                             return -1;
                         }
+                    case "reevaluate":
+                        // Usage : adb shell cmd connectivity reevaluate <netId>
+                        // If netId is omitted, then reevaluate the default network
+                        final String netId = getNextArg();
+                        final NetworkAgentInfo nai;
+                        if (null == netId) {
+                            // Note that the command is running on the wrong thread to call this,
+                            // so this could in principle return stale data. But it can't crash.
+                            nai = getDefaultNetwork();
+                        } else {
+                            // If netId can't be parsed, this throws NumberFormatException, which
+                            // is passed back to adb who prints it.
+                            nai = getNetworkAgentInfoForNetId(Integer.parseInt(netId));
+                        }
+                        if (null == nai) {
+                            pw.println("Unknown network (net ID not found or no default network)");
+                            return 0;
+                        }
+                        Log.d(TAG, "Reevaluating network " + nai.network);
+                        reportNetworkConnectivity(nai.network, !nai.isValidated());
+                        return 0;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -9663,8 +9803,8 @@
         return ((VpnTransportInfo) ti).getType();
     }
 
-    private void maybeUpdateWifiRoamTimestamp(NetworkAgentInfo nai, NetworkCapabilities nc) {
-        if (nai == null) return;
+    private void maybeUpdateWifiRoamTimestamp(@NonNull NetworkAgentInfo nai,
+            @NonNull NetworkCapabilities nc) {
         final TransportInfo prevInfo = nai.networkCapabilities.getTransportInfo();
         final TransportInfo newInfo = nc.getTransportInfo();
         if (!(prevInfo instanceof WifiInfo) || !(newInfo instanceof WifiInfo)) {
diff --git a/service/src/com/android/server/UidOwnerValue.java b/service/src/com/android/server/UidOwnerValue.java
index f89e354..d6c0e0d 100644
--- a/service/src/com/android/server/UidOwnerValue.java
+++ b/service/src/com/android/server/UidOwnerValue.java
@@ -21,14 +21,14 @@
 /** Value type for per uid traffic control configuration map  */
 public class UidOwnerValue extends Struct {
     // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask below.
-    @Field(order = 0, type = Type.U32)
-    public final long iif;
+    @Field(order = 0, type = Type.S32)
+    public final int iif;
 
     // A bitmask of match type.
     @Field(order = 1, type = Type.U32)
     public final long rule;
 
-    public UidOwnerValue(final long iif, final long rule) {
+    public UidOwnerValue(final int iif, final long rule) {
         this.iif = iif;
         this.rule = rule;
     }
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index e1c7b64..5d04632 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -113,12 +113,12 @@
         return "/sys/fs/bpf/net_shared/map_clatd_clat_" + which + "_map";
     }
 
-    private static String makeProgPath(boolean ingress, boolean ether) {
-        String path = "/sys/fs/bpf/net_shared/prog_clatd_schedcls_"
-                + (ingress ? "ingress6" : "egress4")
-                + "_clat_"
+    private static final String CLAT_EGRESS4_RAWIP_PROG_PATH =
+            "/sys/fs/bpf/net_shared/prog_clatd_schedcls_egress4_clat_rawip";
+
+    private static String makeIngressProgPath(boolean ether) {
+        return "/sys/fs/bpf/net_shared/prog_clatd_schedcls_ingress6_clat_"
                 + (ether ? "ether" : "rawip");
-        return path;
     }
 
     @NonNull
@@ -183,8 +183,8 @@
          */
         @NonNull
         public String generateIpv6Address(@NonNull String iface, @NonNull String v4,
-                @NonNull String prefix64) throws IOException {
-            return native_generateIpv6Address(iface, v4, prefix64);
+                @NonNull String prefix64, int mark) throws IOException {
+            return native_generateIpv6Address(iface, v4, prefix64, mark);
         }
 
         /**
@@ -478,7 +478,7 @@
             // tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/...
             // direct-action
             mDeps.tcFilterAddDevBpf(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, (short) ETH_P_IP,
-                    makeProgPath(EGRESS, RAWIP));
+                    CLAT_EGRESS4_RAWIP_PROG_PATH);
         } catch (IOException e) {
             Log.e(TAG, "tc filter add dev (" + tracker.v4ifIndex + "[" + tracker.v4iface
                     + "]) egress prio PRIO_CLAT protocol ip failure: " + e);
@@ -504,7 +504,7 @@
             // tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/...
             // direct-action
             mDeps.tcFilterAddDevBpf(tracker.ifIndex, INGRESS, (short) PRIO_CLAT,
-                    (short) ETH_P_IPV6, makeProgPath(INGRESS, isEthernet));
+                    (short) ETH_P_IPV6, makeIngressProgPath(isEthernet));
         } catch (IOException e) {
             Log.e(TAG, "tc filter add dev (" + tracker.ifIndex + "[" + tracker.iface
                     + "]) ingress prio PRIO_CLAT protocol ipv6 failure: " + e);
@@ -595,13 +595,17 @@
         Log.i(TAG, "untag socket cookie " + cookie);
     }
 
+    private boolean isStarted() {
+        return mClatdTracker != null;
+    }
+
     /**
      * Start clatd for a given interface and NAT64 prefix.
      */
     public String clatStart(final String iface, final int netId,
             @NonNull final IpPrefix nat64Prefix)
             throws IOException {
-        if (mClatdTracker != null) {
+        if (isStarted()) {
             throw new IOException("Clatd is already running on " + mClatdTracker.iface
                     + " (pid " + mClatdTracker.pid + ")");
         }
@@ -625,10 +629,11 @@
         }
 
         // [2] Generate a checksum-neutral IID.
+        final Integer fwmark = getFwmark(netId);
         final String pfx96Str = nat64Prefix.getAddress().getHostAddress();
         final String v6Str;
         try {
-            v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str);
+            v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str, fwmark);
         } catch (IOException e) {
             throw new IOException("no IPv6 addresses were available for clat: " + e);
         }
@@ -672,7 +677,6 @@
         }
 
         // Detect ipv4 mtu.
-        final Integer fwmark = getFwmark(netId);
         final int detectedMtu;
         try {
             detectedMtu = mDeps.detectMtu(pfx96Str,
@@ -833,7 +837,7 @@
      * Stop clatd
      */
     public void clatStop() throws IOException {
-        if (mClatdTracker == null) {
+        if (!isStarted()) {
             throw new IOException("Clatd has not started");
         }
         Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
@@ -895,19 +899,23 @@
     }
 
     /**
-     * Dump the cordinator information.
+     * Dump the coordinator information.
      *
      * @param pw print writer.
      */
     public void dump(@NonNull IndentingPrintWriter pw) {
         // TODO: move map dump to a global place to avoid duplicate dump while there are two or
         // more IPv6 only networks.
-        pw.println("CLAT tracker: " + mClatdTracker.toString());
-        pw.println("Forwarding rules:");
-        pw.increaseIndent();
-        dumpBpfIngress(pw);
-        dumpBpfEgress(pw);
-        pw.decreaseIndent();
+        if (isStarted()) {
+            pw.println("CLAT tracker: " + mClatdTracker);
+            pw.println("Forwarding rules:");
+            pw.increaseIndent();
+            dumpBpfIngress(pw);
+            dumpBpfEgress(pw);
+            pw.decreaseIndent();
+        } else {
+            pw.println("<not started>");
+        }
         pw.println();
     }
 
@@ -923,7 +931,7 @@
     private static native String native_selectIpv4Address(String v4addr, int prefixlen)
             throws IOException;
     private static native String native_generateIpv6Address(String iface, String v4,
-            String prefix64) throws IOException;
+            String prefix64, int mark) throws IOException;
     private static native int native_createTunInterface(String tuniface) throws IOException;
     private static native int native_detectMtu(String platSubnet, int platSuffix, int mark)
             throws IOException;
diff --git a/service/src/com/android/server/connectivity/DscpPolicyValue.java b/service/src/com/android/server/connectivity/DscpPolicyValue.java
index fed96b4..7b11eda 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyValue.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyValue.java
@@ -37,8 +37,8 @@
     @Field(order = 1, type = Type.ByteArray, arraysize = 16)
     public final byte[] dst46;
 
-    @Field(order = 2, type = Type.U32)
-    public final long ifIndex;
+    @Field(order = 2, type = Type.S32)
+    public final int ifIndex;
 
     @Field(order = 3, type = Type.UBE16)
     public final int srcPort;
@@ -116,7 +116,7 @@
         return mask;
     }
 
-    private DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final long ifIndex,
+    private DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex,
             final int srcPort, final int dstPortStart, final int dstPortEnd, final short proto,
             final byte dscp) {
         this.src46 = toAddressField(src46);
@@ -136,7 +136,7 @@
         this.mask = makeMask(this.src46, this.dst46, srcPort, dstPortStart, proto, dscp);
     }
 
-    public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final long ifIndex,
+    public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex,
             final int srcPort, final Range<Integer> dstPort, final short proto,
             final byte dscp) {
         this(src46, dst46, ifIndex, srcPort, dstPort != null ? dstPort.getLower() : -1,
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index c4754eb..2303894 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -23,8 +23,10 @@
 import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
 import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
 
-import android.annotation.IntDef;
+import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkScore;
@@ -35,8 +37,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.MessageUtils;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.StringJoiner;
 
 /**
@@ -49,53 +49,54 @@
 public class FullScore {
     private static final String TAG = FullScore.class.getSimpleName();
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"POLICY_"}, value = {
-            POLICY_IS_VALIDATED,
-            POLICY_IS_VPN,
-            POLICY_EVER_USER_SELECTED,
-            POLICY_ACCEPT_UNVALIDATED,
-            POLICY_IS_UNMETERED
-    })
-    public @interface Policy {
-    }
-
     // Agent-managed policies are in NetworkScore. They start from 1.
     // CS-managed policies, counting from 63 downward
     // This network is validated. CS-managed because the source of truth is in NetworkCapabilities.
     /** @hide */
     public static final int POLICY_IS_VALIDATED = 63;
 
+    // This network has been validated at least once since it was connected.
+    /** @hide */
+    public static final int POLICY_EVER_VALIDATED = 62;
+
     // This is a VPN and behaves as one for scoring purposes.
     /** @hide */
-    public static final int POLICY_IS_VPN = 62;
+    public static final int POLICY_IS_VPN = 61;
 
     // This network has been selected by the user manually from settings or a 3rd party app
     // at least once. @see NetworkAgentConfig#explicitlySelected.
     /** @hide */
-    public static final int POLICY_EVER_USER_SELECTED = 61;
+    public static final int POLICY_EVER_USER_SELECTED = 60;
 
     // The user has indicated in UI that this network should be used even if it doesn't
     // validate. @see NetworkAgentConfig#acceptUnvalidated.
     /** @hide */
-    public static final int POLICY_ACCEPT_UNVALIDATED = 60;
+    public static final int POLICY_ACCEPT_UNVALIDATED = 59;
+
+    // The user explicitly said in UI to avoid this network when unvalidated.
+    // TODO : remove setAvoidUnvalidated and instead disconnect the network when the user
+    // chooses to move away from this network, and remove this flag.
+    /** @hide */
+    public static final int POLICY_AVOIDED_WHEN_UNVALIDATED = 58;
 
     // This network is unmetered. @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED.
     /** @hide */
-    public static final int POLICY_IS_UNMETERED = 59;
+    public static final int POLICY_IS_UNMETERED = 57;
 
     // This network is invincible. This is useful for offers until there is an API to listen
     // to requests.
     /** @hide */
-    public static final int POLICY_IS_INVINCIBLE = 58;
+    public static final int POLICY_IS_INVINCIBLE = 56;
 
-    // This network has been validated at least once since it was connected, but not explicitly
-    // avoided in UI.
-    // TODO : remove setAvoidUnvalidated and instead disconnect the network when the user
-    // chooses to move away from this network, and remove this flag.
-    /** @hide */
-    public static final int POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD = 57;
+    // This network has undergone initial validation.
+    //
+    // The stack considers that any result finding some working connectivity (valid, partial,
+    // captive portal) is an initial validation. Negative result (not valid), however, is not
+    // considered initial validation until {@link ConnectivityService#PROMPT_UNVALIDATED_DELAY_MS}
+    // have elapsed. This is because some networks may spuriously fail for a short time immediately
+    // after associating. If no positive result is found after the timeout has elapsed, then
+    // the network has been evaluated once.
+    public static final int POLICY_EVER_EVALUATED = 55;
 
     // The network agent has communicated that this network no longer functions, and the underlying
     // native network has been destroyed. The network will still be reported to clients as connected
@@ -103,7 +104,7 @@
     // This network should lose to an identical network that has not been destroyed, but should
     // otherwise be scored exactly the same.
     /** @hide */
-    public static final int POLICY_IS_DESTROYED = 56;
+    public static final int POLICY_IS_DESTROYED = 54;
 
     // To help iterate when printing
     @VisibleForTesting
@@ -154,7 +155,9 @@
      * @param caps the NetworkCapabilities of the network
      * @param config the NetworkAgentConfig of the network
      * @param everValidated whether this network has ever validated
+     * @param avoidUnvalidated whether the user said in UI to avoid this network when unvalidated
      * @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad
+     * @param everEvaluated whether this network ever evaluated at least once
      * @param destroyed whether this network has been destroyed pending a replacement connecting
      * @return a FullScore that is appropriate to use for ranking.
      */
@@ -163,18 +166,20 @@
     // connectivity for backward compatibility.
     public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
             @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
-            final boolean everValidated, final boolean yieldToBadWiFi, final boolean destroyed) {
+            final boolean everValidated, final boolean avoidUnvalidated,
+            final boolean yieldToBadWiFi, final boolean everEvaluated, final boolean destroyed) {
         return withPolicies(score.getPolicies(),
                 score.getKeepConnectedReason(),
                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
-                caps.hasTransport(TRANSPORT_VPN),
-                caps.hasCapability(NET_CAPABILITY_NOT_METERED),
-                everValidated,
+                everValidated, caps.hasTransport(TRANSPORT_VPN),
                 config.explicitlySelected,
                 config.acceptUnvalidated,
+                avoidUnvalidated,
+                caps.hasCapability(NET_CAPABILITY_NOT_METERED),
                 yieldToBadWiFi,
-                destroyed,
-                false /* invincible */); // only prospective scores can be invincible
+                false /* invincible */, // only prospective scores can be invincible
+                everEvaluated,
+                destroyed);
     }
 
     /**
@@ -194,25 +199,31 @@
             @NonNull final NetworkCapabilities caps, final boolean yieldToBadWiFi) {
         // If the network offers Internet access, it may validate.
         final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
-        // VPN transports are known in advance.
-        final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
-        // Prospective scores are always unmetered, because unmetered networks are stronger
-        // than metered networks, and it's not known in advance whether the network is metered.
-        final boolean unmetered = true;
         // If the offer may validate, then it should be considered to have validated at some point
         final boolean everValidated = mayValidate;
+        // VPN transports are known in advance.
+        final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
         // The network hasn't been chosen by the user (yet, at least).
         final boolean everUserSelected = false;
         // Don't assume the user will accept unvalidated connectivity.
         final boolean acceptUnvalidated = false;
-        // A network can only be destroyed once it has connected.
-        final boolean destroyed = false;
+        // A prospective network is never avoided when unvalidated, because the user has never
+        // had the opportunity to say so in UI.
+        final boolean avoidUnvalidated = false;
+        // Prospective scores are always unmetered, because unmetered networks are stronger
+        // than metered networks, and it's not known in advance whether the network is metered.
+        final boolean unmetered = true;
         // A prospective score is invincible if the legacy int in the filter is over the maximum
         // score.
         final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
+        // A prospective network will eventually be evaluated.
+        final boolean everEvaluated = true;
+        // A network can only be destroyed once it has connected.
+        final boolean destroyed = false;
         return withPolicies(score.getPolicies(), KEEP_CONNECTED_NONE,
-                mayValidate, vpn, unmetered, everValidated, everUserSelected, acceptUnvalidated,
-                yieldToBadWiFi, destroyed, invincible);
+                mayValidate, everValidated, vpn, everUserSelected,
+                acceptUnvalidated, avoidUnvalidated, unmetered, yieldToBadWiFi,
+                invincible, everEvaluated, destroyed);
     }
 
     /**
@@ -228,18 +239,21 @@
     public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
             @NonNull final NetworkAgentConfig config,
             final boolean everValidated,
+            final boolean avoidUnvalidated,
             final boolean yieldToBadWifi,
+            final boolean everEvaluated,
             final boolean destroyed) {
         return withPolicies(mPolicies, mKeepConnectedReason,
                 caps.hasCapability(NET_CAPABILITY_VALIDATED),
-                caps.hasTransport(TRANSPORT_VPN),
-                caps.hasCapability(NET_CAPABILITY_NOT_METERED),
-                everValidated,
+                everValidated, caps.hasTransport(TRANSPORT_VPN),
                 config.explicitlySelected,
                 config.acceptUnvalidated,
+                avoidUnvalidated,
+                caps.hasCapability(NET_CAPABILITY_NOT_METERED),
                 yieldToBadWifi,
-                destroyed,
-                false /* invincible */); // only prospective scores can be invincible
+                false /* invincible */, // only prospective scores can be invincible
+                everEvaluated,
+                destroyed);
     }
 
     // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
@@ -248,24 +262,28 @@
     private static FullScore withPolicies(final long externalPolicies,
             @KeepConnectedReason final int keepConnectedReason,
             final boolean isValidated,
-            final boolean isVpn,
-            final boolean isUnmetered,
             final boolean everValidated,
+            final boolean isVpn,
             final boolean everUserSelected,
             final boolean acceptUnvalidated,
+            final boolean avoidUnvalidated,
+            final boolean isUnmetered,
             final boolean yieldToBadWiFi,
-            final boolean destroyed,
-            final boolean invincible) {
+            final boolean invincible,
+            final boolean everEvaluated,
+            final boolean destroyed) {
         return new FullScore((externalPolicies & EXTERNAL_POLICIES_MASK)
                 | (isValidated       ? 1L << POLICY_IS_VALIDATED : 0)
+                | (everValidated     ? 1L << POLICY_EVER_VALIDATED : 0)
                 | (isVpn             ? 1L << POLICY_IS_VPN : 0)
-                | (isUnmetered       ? 1L << POLICY_IS_UNMETERED : 0)
-                | (everValidated     ? 1L << POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD : 0)
                 | (everUserSelected  ? 1L << POLICY_EVER_USER_SELECTED : 0)
                 | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0)
+                | (avoidUnvalidated  ? 1L << POLICY_AVOIDED_WHEN_UNVALIDATED : 0)
+                | (isUnmetered       ? 1L << POLICY_IS_UNMETERED : 0)
                 | (yieldToBadWiFi    ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0)
-                | (destroyed         ? 1L << POLICY_IS_DESTROYED : 0)
-                | (invincible        ? 1L << POLICY_IS_INVINCIBLE : 0),
+                | (invincible        ? 1L << POLICY_IS_INVINCIBLE : 0)
+                | (everEvaluated     ? 1L << POLICY_EVER_EVALUATED : 0)
+                | (destroyed         ? 1L << POLICY_IS_DESTROYED : 0),
                 keepConnectedReason);
     }
 
@@ -318,6 +336,35 @@
                 + 5 * mKeepConnectedReason;
     }
 
+    /**
+     * Returns a short but human-readable string of updates from an older score.
+     * @param old the old capabilities to diff from
+     * @return a string fit for logging differences, or null if no differences.
+     *         this method cannot return the empty string.
+     */
+    @Nullable
+    public String describeDifferencesFrom(@Nullable final FullScore old) {
+        final long oldPolicies = null == old ? 0 : old.mPolicies;
+        final long changed = oldPolicies ^ mPolicies;
+        if (0 == changed) return null;
+        // If the control reaches here, there are changes (additions, removals, or both) so
+        // the code below is guaranteed to add something to the string and can't return "".
+        final long removed = oldPolicies & changed;
+        final long added = mPolicies & changed;
+        final StringBuilder sb = new StringBuilder();
+        if (0 != removed) {
+            sb.append("-");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, removed,
+                    FullScore::policyNameOf, "-");
+        }
+        if (0 != added) {
+            sb.append("+");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, added,
+                    FullScore::policyNameOf, "+");
+        }
+        return sb.toString();
+    }
+
     // Example output :
     // Score(Policies : EVER_USER_SELECTED&IS_VALIDATED ; KeepConnected : )
     @Override
diff --git a/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
new file mode 100644
index 0000000..58196f7
--- /dev/null
+++ b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2016 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.server.connectivity;
+
+import static android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+import android.annotation.NonNull;
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.ConnectivityResources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.connectivity.resources.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.DeviceConfigUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * A class to encapsulate management of the "Smart Networking" capability of
+ * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
+ * certain critical link failures occur.
+ *
+ * This enables the device to switch to another form of connectivity, like
+ * mobile, if it's available and working.
+ *
+ * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
+ * Handler' whenever the computed "avoid bad wifi" value changes.
+ *
+ * Disabling this reverts the device to a level of networking sophistication
+ * circa 2012-13 by disabling disparate code paths each of which contribute to
+ * maintaining continuous, working Internet connectivity.
+ *
+ * @hide
+ */
+public class MultinetworkPolicyTracker {
+    private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
+
+    // See Dependencies#getConfigActivelyPreferBadWifi
+    public static final String CONFIG_ACTIVELY_PREFER_BAD_WIFI = "actively_prefer_bad_wifi";
+
+    private final Context mContext;
+    private final ConnectivityResources mResources;
+    private final Handler mHandler;
+    private final Runnable mAvoidBadWifiCallback;
+    private final List<Uri> mSettingsUris;
+    private final ContentResolver mResolver;
+    private final SettingObserver mSettingObserver;
+    private final BroadcastReceiver mBroadcastReceiver;
+
+    private volatile boolean mAvoidBadWifi = true;
+    private volatile int mMeteredMultipathPreference;
+    private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private volatile long mTestAllowBadWifiUntilMs = 0;
+
+    /**
+     * Dependencies for testing
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * @see DeviceConfigUtils#getDeviceConfigPropertyInt
+         */
+        protected int getConfigActivelyPreferBadWifi() {
+            // CONFIG_ACTIVELY_PREFER_BAD_WIFI is not a feature to be rolled out, but an override
+            // for tests and an emergency kill switch (which could force the behavior on OR off).
+            // As such it uses a -1/null/1 scheme, but features should use
+            // DeviceConfigUtils#isFeatureEnabled instead, to make sure rollbacks disable the
+            // feature before it's ready on R and before.
+            return DeviceConfig.getInt(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                    CONFIG_ACTIVELY_PREFER_BAD_WIFI, 0);
+        }
+
+        /**
+         @see DeviceConfig#addOnPropertiesChangedListener
+         */
+        protected void addOnDevicePropertiesChangedListener(@NonNull final Executor executor,
+                @NonNull final DeviceConfig.OnPropertiesChangedListener listener) {
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                    executor, listener);
+        }
+
+        @VisibleForTesting
+        @NonNull
+        protected Resources getResourcesForActiveSubId(
+                @NonNull final ConnectivityResources resources, final int activeSubId) {
+            return SubscriptionManager.getResourcesForSubId(
+                    resources.getResourcesContext(), activeSubId);
+        }
+    }
+    private final Dependencies mDeps;
+
+    /**
+     * Whether to prefer bad wifi to a network that yields to bad wifis, even if it never validated
+     *
+     * This setting only makes sense if the system is configured not to avoid bad wifis, i.e.
+     * if mAvoidBadWifi is true. If it's not, then no network ever yields to bad wifis
+     * ({@see FullScore#POLICY_YIELD_TO_BAD_WIFI}) and this setting has therefore no effect.
+     *
+     * If this is false, when ranking a bad wifi that never validated against cell data (or any
+     * network that yields to bad wifis), the ranker will prefer cell data. It will prefer wifi
+     * if wifi loses validation later. This behavior avoids the device losing internet access when
+     * walking past a wifi network with no internet access.
+     * This is the default behavior up to Android T, but it can be overridden through an overlay
+     * to behave like below.
+     *
+     * If this is true, then in the same scenario, the ranker will prefer cell data until
+     * the wifi completes its first validation attempt (or the attempt times out after
+     * ConnectivityService#PROMPT_UNVALIDATED_DELAY_MS), then it will prefer the wifi even if it
+     * doesn't provide internet access, unless there is a captive portal on that wifi.
+     * This is the behavior in U and above.
+     */
+    private boolean mActivelyPreferBadWifi;
+
+    // Mainline module can't use internal HandlerExecutor, so add an identical executor here.
+    private static class HandlerExecutor implements Executor {
+        @NonNull
+        private final Handler mHandler;
+
+        HandlerExecutor(@NonNull Handler handler) {
+            mHandler = handler;
+        }
+        @Override
+        public void execute(Runnable command) {
+            if (!mHandler.post(command)) {
+                throw new RejectedExecutionException(mHandler + " is shutting down");
+            }
+        }
+    }
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @VisibleForTesting @TargetApi(Build.VERSION_CODES.S)
+    protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
+            implements TelephonyCallback.ActiveDataSubscriptionIdListener {
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            mActiveSubId = subId;
+            reevaluateInternal();
+        }
+    }
+
+    public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
+        this(ctx, handler, avoidBadWifiCallback, new Dependencies());
+    }
+
+    public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback,
+            Dependencies deps) {
+        mContext = ctx;
+        mResources = new ConnectivityResources(ctx);
+        mHandler = handler;
+        mAvoidBadWifiCallback = avoidBadWifiCallback;
+        mDeps = deps;
+        mSettingsUris = Arrays.asList(
+                Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+                Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+        mResolver = mContext.getContentResolver();
+        mSettingObserver = new SettingObserver();
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                reevaluateInternal();
+            }
+        };
+
+        updateAvoidBadWifi();
+        updateMeteredMultipathPreference();
+    }
+
+    // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+    @TargetApi(Build.VERSION_CODES.S)
+    public void start() {
+        for (Uri uri : mSettingsUris) {
+            mResolver.registerContentObserver(uri, false, mSettingObserver);
+        }
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter,
+                null /* broadcastPermission */, mHandler);
+
+        final Executor handlerExecutor = new HandlerExecutor(mHandler);
+        mContext.getSystemService(TelephonyManager.class).registerTelephonyCallback(
+                handlerExecutor, new ActiveDataSubscriptionIdListener());
+        mDeps.addOnDevicePropertiesChangedListener(handlerExecutor,
+                properties -> reevaluateInternal());
+
+        reevaluate();
+    }
+
+    public void shutdown() {
+        mResolver.unregisterContentObserver(mSettingObserver);
+
+        mContext.unregisterReceiver(mBroadcastReceiver);
+    }
+
+    public boolean getAvoidBadWifi() {
+        return mAvoidBadWifi;
+    }
+
+    public boolean getActivelyPreferBadWifi() {
+        return mActivelyPreferBadWifi;
+    }
+
+    // TODO: move this to MultipathPolicyTracker.
+    public int getMeteredMultipathPreference() {
+        return mMeteredMultipathPreference;
+    }
+
+    /**
+     * Whether the device or carrier configuration disables avoiding bad wifi by default.
+     */
+    public boolean configRestrictsAvoidBadWifi() {
+        final boolean allowBadWifi = mTestAllowBadWifiUntilMs > 0
+                && mTestAllowBadWifiUntilMs > System.currentTimeMillis();
+        // If the config returns true, then avoid bad wifi design can be controlled by the
+        // NETWORK_AVOID_BAD_WIFI setting.
+        if (allowBadWifi) return true;
+
+        return mDeps.getResourcesForActiveSubId(mResources, mActiveSubId)
+                .getInteger(R.integer.config_networkAvoidBadWifi) == 0;
+    }
+
+    /**
+     * Whether the device config prefers bad wifi actively, when it doesn't avoid them
+     *
+     * This is only relevant when the device is configured not to avoid bad wifis. In this
+     * case, "actively" preferring a bad wifi means that the device will switch to a bad
+     * wifi it just connected to, as long as it's not a captive portal.
+     *
+     * On U and above this always returns true. On T and below it reads a configuration option.
+     */
+    public boolean configActivelyPrefersBadWifi() {
+        // See the definition of config_activelyPreferBadWifi for a description of its meaning.
+        // On U and above, the config is ignored, and bad wifi is always actively preferred.
+        if (SdkLevel.isAtLeastU()) return true;
+
+        // On T and below, 1 means to actively prefer bad wifi, 0 means not to prefer
+        // bad wifi (only stay stuck on it if already on there). This implementation treats
+        // any non-0 value like 1, on the assumption that anybody setting it non-zero wants
+        // the newer behavior.
+        return 0 != mDeps.getResourcesForActiveSubId(mResources, mActiveSubId)
+                .getInteger(R.integer.config_activelyPreferBadWifi);
+    }
+
+    /**
+     * Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
+     * The value works when the time set is more than {@link System.currentTimeMillis()}.
+     */
+    public void setTestAllowBadWifiUntil(long timeMs) {
+        Log.d(TAG, "setTestAllowBadWifiUntil: " + timeMs);
+        mTestAllowBadWifiUntilMs = timeMs;
+        reevaluateInternal();
+    }
+
+    /**
+     * Whether we should display a notification when wifi becomes unvalidated.
+     */
+    public boolean shouldNotifyWifiUnvalidated() {
+        return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
+    }
+
+    public String getAvoidBadWifiSetting() {
+        return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
+    }
+
+    /**
+     * Returns whether device config says the device should actively prefer bad wifi.
+     *
+     * {@see #configActivelyPrefersBadWifi} for a description of what this does. This device
+     * config overrides that config overlay.
+     *
+     * @return True on Android U and above.
+     *         True if device config says to actively prefer bad wifi.
+     *         False if device config says not to actively prefer bad wifi.
+     *         null if device config doesn't have an opinion (then fall back on the resource).
+     */
+    public Boolean deviceConfigActivelyPreferBadWifi() {
+        if (SdkLevel.isAtLeastU()) return true;
+        switch (mDeps.getConfigActivelyPreferBadWifi()) {
+            case 1:
+                return Boolean.TRUE;
+            case -1:
+                return Boolean.FALSE;
+            default:
+                return null;
+        }
+    }
+
+    @VisibleForTesting
+    public void reevaluate() {
+        mHandler.post(this::reevaluateInternal);
+    }
+
+    /**
+     * Reevaluate the settings. Must be called on the handler thread.
+     */
+    private void reevaluateInternal() {
+        if (updateAvoidBadWifi() && mAvoidBadWifiCallback != null) {
+            mAvoidBadWifiCallback.run();
+        }
+        updateMeteredMultipathPreference();
+    }
+
+    public boolean updateAvoidBadWifi() {
+        final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
+        final boolean prevAvoid = mAvoidBadWifi;
+        mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
+
+        final boolean prevActive = mActivelyPreferBadWifi;
+        final Boolean deviceConfigPreferBadWifi = deviceConfigActivelyPreferBadWifi();
+        if (null == deviceConfigPreferBadWifi) {
+            mActivelyPreferBadWifi = configActivelyPrefersBadWifi();
+        } else {
+            mActivelyPreferBadWifi = deviceConfigPreferBadWifi;
+        }
+
+        return mAvoidBadWifi != prevAvoid || mActivelyPreferBadWifi != prevActive;
+    }
+
+    /**
+     * The default (device and carrier-dependent) value for metered multipath preference.
+     */
+    public int configMeteredMultipathPreference() {
+        return mDeps.getResourcesForActiveSubId(mResources, mActiveSubId)
+                .getInteger(R.integer.config_networkMeteredMultipathPreference);
+    }
+
+    public void updateMeteredMultipathPreference() {
+        String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
+        try {
+            mMeteredMultipathPreference = Integer.parseInt(setting);
+        } catch (NumberFormatException e) {
+            mMeteredMultipathPreference = configMeteredMultipathPreference();
+        }
+    }
+
+    private class SettingObserver extends ContentObserver {
+        public SettingObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            Log.wtf(TAG, "Should never be reached.");
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (!mSettingsUris.contains(uri)) {
+                Log.wtf(TAG, "Unexpected settings observation: " + uri);
+            }
+            reevaluate();
+        }
+    }
+}
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index a57e992..2ac2ad3 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -22,6 +22,7 @@
 import static com.android.net.module.util.CollectionUtils.contains;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.ConnectivityManager;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -420,7 +421,7 @@
      * This is necessary because the LinkProperties in mNetwork come from the transport layer, which
      * has no idea that 464xlat is running on top of it.
      */
-    public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
+    public void fixupLinkProperties(@Nullable LinkProperties oldLp, @NonNull LinkProperties lp) {
         // This must be done even if clatd is not running, because otherwise shouldStartClat would
         // never return true.
         lp.setNat64Prefix(selectNat64Prefix());
@@ -433,6 +434,8 @@
         }
 
         Log.d(TAG, "clatd running, updating NAI for " + mIface);
+        // oldLp can't be null here since shouldStartClat checks null LinkProperties to start clat.
+        // Thus, the status won't pass isRunning check if the oldLp is null.
         for (LinkProperties stacked: oldLp.getStackedLinks()) {
             if (Objects.equals(mIface, stacked.getInterfaceName())) {
                 lp.addStackedLink(stacked);
@@ -540,6 +543,9 @@
      */
     public void dump(IndentingPrintWriter pw) {
         if (SdkLevel.isAtLeastT()) {
+            // Dump ClatCoordinator information while clatd has been started but not running. The
+            // reason is that it helps to have more information if clatd is started but the
+            // v4-* interface doesn't bring up. See #isStarted, #isRunning.
             if (isStarted()) {
                 pw.println("ClatCoordinator:");
                 pw.increaseIndent();
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index d4d7d96..85282cb 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -61,6 +61,7 @@
 import android.util.Pair;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.WakeupMessage;
 import com.android.modules.utils.build.SdkLevel;
@@ -377,6 +378,34 @@
         return 0L != mPartialConnectivityTime;
     }
 
+    // Timestamp (SystemClock.elapsedRealTime()) at which the first validation attempt concluded,
+    // or timed out after {@link ConnectivityService#PROMPT_UNVALIDATED_DELAY_MS}. 0 if not yet.
+    private long mFirstEvaluationConcludedTime;
+
+    /**
+     * Notify this NAI that this network has been evaluated.
+     *
+     * The stack considers that any result finding some working connectivity (valid, partial,
+     * captive portal) is an initial validation. Negative result (not valid), however, is not
+     * considered initial validation until {@link ConnectivityService#PROMPT_UNVALIDATED_DELAY_MS}
+     * have elapsed. This is because some networks may spuriously fail for a short time immediately
+     * after associating. If no positive result is found after the timeout has elapsed, then
+     * the network has been evaluated once.
+     *
+     * @return true the first time this is called on this object, then always returns false.
+     */
+    public boolean setEvaluated() {
+        if (0L != mFirstEvaluationConcludedTime) return false;
+        mFirstEvaluationConcludedTime = SystemClock.elapsedRealtime();
+        return true;
+    }
+
+    /** When this network ever concluded its first evaluation, or 0 if this never happened. */
+    @VisibleForTesting
+    public long getFirstEvaluationConcludedTime() {
+        return mFirstEvaluationConcludedTime;
+    }
+
     // Delay between when the network is disconnected and when the native network is destroyed.
     public int teardownDelayMs;
 
@@ -392,6 +421,9 @@
     // URL, Terms & Conditions URL, and network friendly name.
     public CaptivePortalData networkAgentPortalData;
 
+    // Indicate whether this device has the automotive feature.
+    private final boolean mHasAutomotiveFeature;
+
     /**
      * Sets the capabilities sent by the agent for later retrieval.
      *
@@ -433,9 +465,8 @@
                     + networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
             nc.setOwnerUid(networkCapabilities.getOwnerUid());
         }
-        restrictCapabilitiesFromNetworkAgent(nc, creatorUid,
-                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE),
-                carrierPrivilegeAuthenticator);
+        restrictCapabilitiesFromNetworkAgent(
+                nc, creatorUid, mHasAutomotiveFeature, carrierPrivilegeAuthenticator);
         return nc;
     }
 
@@ -604,6 +635,8 @@
                 ? nc.getUnderlyingNetworks().toArray(new Network[0])
                 : null;
         mCreationTime = System.currentTimeMillis();
+        mHasAutomotiveFeature =
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
 
     private class AgentDeathMonitor implements IBinder.DeathRecipient {
@@ -970,8 +1003,7 @@
             @NonNull final NetworkCapabilities nc) {
         final NetworkCapabilities oldNc = networkCapabilities;
         networkCapabilities = nc;
-        mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, everValidatedForYield(),
-                yieldToBadWiFi(), isDestroyed());
+        updateScoreForNetworkAgentUpdate();
         final NetworkMonitorManager nm = mNetworkMonitor;
         if (nm != null) {
             nm.notifyNetworkCapabilitiesChanged(nc);
@@ -1173,8 +1205,11 @@
      * Mix-in the ConnectivityService-managed bits in the score.
      */
     public void setScore(final NetworkScore score) {
+        final FullScore oldScore = mScore;
         mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig,
-                everValidatedForYield(), yieldToBadWiFi(), isDestroyed());
+                everValidated(), 0L != getAvoidUnvalidated(), yieldToBadWiFi(),
+                0L != mFirstEvaluationConcludedTime, isDestroyed());
+        maybeLogDifferences(oldScore);
     }
 
     /**
@@ -1183,12 +1218,22 @@
      * Call this after changing any data that might affect the score (e.g., agent config).
      */
     public void updateScoreForNetworkAgentUpdate() {
+        final FullScore oldScore = mScore;
         mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig,
-                everValidatedForYield(), yieldToBadWiFi(), isDestroyed());
+                everValidated(), 0L != getAvoidUnvalidated(), yieldToBadWiFi(),
+                0L != mFirstEvaluationConcludedTime, isDestroyed());
+        maybeLogDifferences(oldScore);
     }
 
-    private boolean everValidatedForYield() {
-        return everValidated() && 0L == mAvoidUnvalidated;
+    /**
+     * Prints score differences to logcat, if any.
+     * @param oldScore the old score. Differences from |oldScore| to |this| are logged, if any.
+     */
+    public void maybeLogDifferences(final FullScore oldScore) {
+        final String differences = mScore.describeDifferencesFrom(oldScore);
+        if (null != differences) {
+            Log.i(TAG, "Update score for net " + network + " : " + differences);
+        }
     }
 
     /**
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 155f6c4..45da0ea 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -394,8 +394,9 @@
         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     }
 
-    @VisibleForTesting
-    static String tagFor(int id) {
+    /** Get the logging tag for a notification ID */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static String tagFor(int id) {
         return String.format("ConnectivityNotification:%d", id);
     }
 
diff --git a/service/src/com/android/server/connectivity/NetworkRanker.java b/service/src/com/android/server/connectivity/NetworkRanker.java
index babc353..d94c8dc 100644
--- a/service/src/com/android/server/connectivity/NetworkRanker.java
+++ b/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -26,8 +27,10 @@
 
 import static com.android.net.module.util.CollectionUtils.filter;
 import static com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED;
+import static com.android.server.connectivity.FullScore.POLICY_AVOIDED_WHEN_UNVALIDATED;
+import static com.android.server.connectivity.FullScore.POLICY_EVER_EVALUATED;
 import static com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED;
-import static com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD;
+import static com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED;
 import static com.android.server.connectivity.FullScore.POLICY_IS_DESTROYED;
 import static com.android.server.connectivity.FullScore.POLICY_IS_INVINCIBLE;
 import static com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED;
@@ -38,18 +41,39 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.CollectionUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Predicate;
 
 /**
  * A class that knows how to find the best network matching a request out of a list of networks.
  */
 public class NetworkRanker {
+    /**
+     * Home for all configurations of NetworkRanker
+     */
+    public static final class Configuration {
+        private final boolean mActivelyPreferBadWifi;
+
+        public Configuration(final boolean activelyPreferBadWifi) {
+            this.mActivelyPreferBadWifi = activelyPreferBadWifi;
+        }
+
+        /**
+         * @see MultinetworkPolicyTracker#getActivelyPreferBadWifi()
+         */
+        public boolean activelyPreferBadWifi() {
+            return mActivelyPreferBadWifi;
+        }
+    }
+    @NonNull private volatile Configuration mConf;
+
     // Historically the legacy ints have been 0~100 in principle (though the highest score in
     // AOSP has always been 90). This is relied on by VPNs that send a legacy score of 101.
     public static final int LEGACY_INT_MAX = 100;
@@ -64,7 +88,22 @@
         NetworkCapabilities getCapsNoCopy();
     }
 
-    public NetworkRanker() { }
+    public NetworkRanker(@NonNull final Configuration conf) {
+        // Because mConf is volatile, the only way it could be seen null would be an access to it
+        // on some other thread during this constructor. But this is not possible because mConf is
+        // private and `this` doesn't escape this constructor.
+        setConfiguration(conf);
+    }
+
+    public void setConfiguration(@NonNull final Configuration conf) {
+        mConf = Objects.requireNonNull(conf);
+    }
+
+    // There shouldn't be a use case outside of testing
+    @VisibleForTesting
+    public Configuration getConfiguration() {
+        return mConf;
+    }
 
     /**
      * Find the best network satisfying this request among the list of passed networks.
@@ -103,9 +142,42 @@
         }
     }
 
-    private <T extends Scoreable> boolean isBadWiFi(@NonNull final T candidate) {
-        return candidate.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
-                && candidate.getCapsNoCopy().hasTransport(TRANSPORT_WIFI);
+    /**
+     * Returns whether the wifi passed as an argument is a preferred network to yielding cell.
+     *
+     * When comparing bad wifi to cell with POLICY_YIELD_TO_BAD_WIFI, it may be necessary to
+     * know if a particular bad wifi is preferred to such a cell network. This method computes
+     * and returns this.
+     *
+     * @param candidate a bad wifi to evaluate
+     * @return whether this candidate is preferred to cell with POLICY_YIELD_TO_BAD_WIFI
+     */
+    private <T extends Scoreable> boolean isPreferredBadWiFi(@NonNull final T candidate) {
+        final FullScore score = candidate.getScore();
+        final NetworkCapabilities caps = candidate.getCapsNoCopy();
+
+        // Whatever the policy, only WiFis can be preferred bad WiFis.
+        if (!caps.hasTransport(TRANSPORT_WIFI)) return false;
+        // Validated networks aren't bad networks, so a fortiori can't be preferred bad WiFis.
+        if (score.hasPolicy(POLICY_IS_VALIDATED)) return false;
+        // A WiFi that the user explicitly wanted to avoid in UI is never a preferred bad WiFi.
+        if (score.hasPolicy(POLICY_AVOIDED_WHEN_UNVALIDATED)) return false;
+
+        if (mConf.activelyPreferBadWifi()) {
+            // If a network is still evaluating, don't prefer it.
+            if (!score.hasPolicy(POLICY_EVER_EVALUATED)) return false;
+
+            // If a network is not a captive portal, then prefer it.
+            if (!caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return true;
+
+            // If it's a captive portal, prefer it if it previously validated but is no longer
+            // validated (i.e., the user logged in in the past, but later the portal closed).
+            return score.hasPolicy(POLICY_EVER_VALIDATED);
+        } else {
+            // Under the original "prefer bad WiFi" policy, only networks that have ever validated
+            // are preferred.
+            return score.hasPolicy(POLICY_EVER_VALIDATED);
+        }
     }
 
     /**
@@ -128,7 +200,7 @@
             // No network with the policy : do nothing.
             return;
         }
-        if (!CollectionUtils.any(rejected, n -> isBadWiFi(n))) {
+        if (!CollectionUtils.any(rejected, n -> isPreferredBadWiFi(n))) {
             // No bad WiFi : do nothing.
             return;
         }
@@ -138,7 +210,7 @@
             // wifis by the following policies (e.g. exiting).
             final ArrayList<T> acceptedYielders = new ArrayList<>(accepted);
             final ArrayList<T> rejectedWithBadWiFis = new ArrayList<>(rejected);
-            partitionInto(rejectedWithBadWiFis, n -> isBadWiFi(n), accepted, rejected);
+            partitionInto(rejectedWithBadWiFis, n -> isPreferredBadWiFi(n), accepted, rejected);
             accepted.addAll(acceptedYielders);
             return;
         }
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index fd1ed60..ff979d8 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -52,6 +52,8 @@
 import android.net.UidRange;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -98,6 +100,7 @@
     private final Dependencies mDeps;
     private final Context mContext;
     private final BpfNetMaps mBpfNetMaps;
+    private final HandlerThread mThread;
 
     private static final ProcessShim sProcessShim = ProcessShimImpl.newInstance();
 
@@ -175,6 +178,22 @@
                 final String[] pkgList =
                         intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 onExternalApplicationsAvailable(pkgList);
+            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+                final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+                // User should be filled for below intents, check the existence.
+                if (user == null) {
+                    Log.wtf(TAG, action + " broadcast without EXTRA_USER");
+                    return;
+                }
+                onUserAdded(user);
+            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+                // User should be filled for below intents, check the existence.
+                if (user == null) {
+                    Log.wtf(TAG, action + " broadcast without EXTRA_USER");
+                    return;
+                }
+                onUserRemoved(user);
             } else {
                 Log.wtf(TAG, "received unexpected intent: " + action);
             }
@@ -243,14 +262,15 @@
     }
 
     public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
-            @NonNull final BpfNetMaps bpfNetMaps) {
-        this(context, netd, bpfNetMaps, new Dependencies());
+            @NonNull final BpfNetMaps bpfNetMaps, @NonNull final HandlerThread thread) {
+        this(context, netd, bpfNetMaps, new Dependencies(), thread);
     }
 
     @VisibleForTesting
     PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
             @NonNull final BpfNetMaps bpfNetMaps,
-            @NonNull final Dependencies deps) {
+            @NonNull final Dependencies deps,
+            @NonNull final HandlerThread thread) {
         mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
@@ -258,6 +278,14 @@
         mDeps = deps;
         mContext = context;
         mBpfNetMaps = bpfNetMaps;
+        mThread = thread;
+    }
+
+    private void ensureRunningOnHandlerThread() {
+        if (mThread.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on Handler thread: " + Thread.currentThread().getName());
+        }
     }
 
     private int getPackageNetdNetworkPermission(@NonNull final PackageInfo app) {
@@ -389,14 +417,14 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
+        final Handler handler = new Handler(mThread.getLooper());
         final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
         final IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         userAllContext.registerReceiver(
-                mIntentReceiver, intentFilter, null /* broadcastPermission */,
-                null /* scheduler */);
+                mIntentReceiver, intentFilter, null /* broadcastPermission */, handler);
 
         // Listen to EXTERNAL_APPLICATIONS_AVAILABLE is that an app becoming available means it may
         // need to gain a permission. But an app that becomes unavailable can neither gain nor lose
@@ -405,15 +433,21 @@
         final IntentFilter externalIntentFilter =
                 new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         userAllContext.registerReceiver(
-                mIntentReceiver, externalIntentFilter, null /* broadcastPermission */,
-                null /* scheduler */);
+                mIntentReceiver, externalIntentFilter, null /* broadcastPermission */, handler);
+
+        // Listen for user add/remove.
+        final IntentFilter userIntentFilter = new IntentFilter();
+        userIntentFilter.addAction(Intent.ACTION_USER_ADDED);
+        userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        userAllContext.registerReceiver(
+                mIntentReceiver, userIntentFilter, null /* broadcastPermission */, handler);
 
         // Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
         mDeps.registerContentObserver(
                 userAllContext,
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS),
                 false /* notifyForDescendants */,
-                new ContentObserver(null) {
+                new ContentObserver(handler) {
                     @Override
                     public void onChange(boolean selfChange) {
                         onSettingChanged();
@@ -517,6 +551,7 @@
     }
 
     private void sendUidsNetworkPermission(SparseIntArray uids, boolean add) {
+        ensureRunningOnHandlerThread();
         List<Integer> network = new ArrayList<>();
         List<Integer> system = new ArrayList<>();
         for (int i = 0; i < uids.size(); i++) {
@@ -547,7 +582,8 @@
      *
      * @hide
      */
-    public synchronized void onUserAdded(@NonNull UserHandle user) {
+    @VisibleForTesting
+    synchronized void onUserAdded(@NonNull UserHandle user) {
         mUsers.add(user);
 
         final List<PackageInfo> apps = getInstalledPackagesAsUser(user);
@@ -578,7 +614,8 @@
      *
      * @hide
      */
-    public synchronized void onUserRemoved(@NonNull UserHandle user) {
+    @VisibleForTesting
+    synchronized void onUserRemoved(@NonNull UserHandle user) {
         mUsers.remove(user);
 
         // Remove uids network permissions that belongs to the user.
@@ -758,7 +795,8 @@
      *
      * @hide
      */
-    public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
+    @VisibleForTesting
+    synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
         // Update uid permission.
         updateAppIdTrafficPermission(uid);
         // Get the appId permission from all users then send the latest permission to netd.
@@ -821,7 +859,8 @@
      *
      * @hide
      */
-    public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
+    @VisibleForTesting
+    synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
         // Update uid permission.
         updateAppIdTrafficPermission(uid);
         // Get the appId permission from all users then send the latest permission to netd.
@@ -1115,6 +1154,7 @@
      */
     @VisibleForTesting
     void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
+        ensureRunningOnHandlerThread();
         final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
         final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
         final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
@@ -1173,13 +1213,13 @@
 
     /** Should only be used by unit tests */
     @VisibleForTesting
-    public Set<UidRange> getVpnInterfaceUidRanges(String iface) {
+    public synchronized Set<UidRange> getVpnInterfaceUidRanges(String iface) {
         return mVpnInterfaceUidRanges.get(iface);
     }
 
     /** Should only be used by unit tests */
     @VisibleForTesting
-    public Set<UidRange> getVpnLockdownUidRanges() {
+    synchronized Set<UidRange> getVpnLockdownUidRanges() {
         return mVpnLockdownUidRanges.getSet();
     }
 
@@ -1255,8 +1295,10 @@
         pw.println();
         pw.println("Lockdown filtering rules:");
         pw.increaseIndent();
-        for (final UidRange range : mVpnLockdownUidRanges.getSet()) {
-            pw.println("UIDs: " + range);
+        synchronized (this) {
+            for (final UidRange range : mVpnLockdownUidRanges.getSet()) {
+                pw.println("UIDs: " + range);
+            }
         }
         pw.decreaseIndent();
 
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index c30e1d3..7b374d2 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -36,6 +36,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
@@ -1370,4 +1371,23 @@
 
         assertEquals(expectedNcBuilder.build(), restrictedNc);
     }
+
+    @Test
+    public void testDescribeCapsDifferences() throws Exception {
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_MMS)
+                .addCapability(NET_CAPABILITY_OEM_PAID)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
+                .addCapability(NET_CAPABILITY_SUPL)
+                .addCapability(NET_CAPABILITY_VALIDATED)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        assertEquals("-MMS-OEM_PAID+SUPL+VALIDATED+CAPTIVE_PORTAL",
+                nc2.describeCapsDifferencesFrom(nc1));
+        assertEquals("-SUPL-VALIDATED-CAPTIVE_PORTAL+MMS+OEM_PAID",
+                nc1.describeCapsDifferencesFrom(nc2));
+    }
 }
diff --git a/tests/common/java/android/net/VpnTransportInfoTest.java b/tests/common/java/android/net/VpnTransportInfoTest.java
new file mode 100644
index 0000000..161f9ee
--- /dev/null
+++ b/tests/common/java/android/net/VpnTransportInfoTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.net;
+
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
+
+import static com.android.testutils.MiscAsserts.assertThrows;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+
+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.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.ConnectivityModuleTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@ConnectivityModuleTest
+public class VpnTransportInfoTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+    @Test
+    public void testParceling() {
+        final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+        assertParcelingIsLossless(v);
+
+        final VpnTransportInfo v2 =
+                new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345", true);
+        assertParcelingIsLossless(v2);
+    }
+
+    @Test
+    public void testEqualsAndHashCode() {
+        String session1 = "12345";
+        String session2 = "6789";
+        final VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+        final VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
+        final VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+        final VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
+        final VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
+        final VpnTransportInfo v16 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1, true);
+        final VpnTransportInfo v17 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1, true);
+        final VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
+
+        final VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+        final VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+        final VpnTransportInfo v33 = v16.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+        final VpnTransportInfo v34 = v17.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+
+        assertNotEquals(v11, v12);
+        assertNotEquals(v13, v14);
+        assertNotEquals(v14, v15);
+        assertNotEquals(v14, v21);
+        assertNotEquals(v15, v16);
+
+        assertEquals(v11, v13);
+        assertEquals(v31, v32);
+        assertEquals(v33, v34);
+        assertEquals(v11.hashCode(), v13.hashCode());
+        assertEquals(v16.hashCode(), v17.hashCode());
+        assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
+        assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId());
+    }
+
+    @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testGetBypassable_beforeU() {
+        final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+        assertThrows(UnsupportedOperationException.class, () -> v.getBypassable());
+    }
+
+    @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testGetBypassable_afterU() {
+        final VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+        assertFalse(v.getBypassable());
+
+        final VpnTransportInfo v2 =
+                new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345", true);
+        assertTrue(v2.getBypassable());
+    }
+}
diff --git a/tests/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
index fa4adcb..8c2ec40 100644
--- a/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -33,6 +33,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
 import org.junit.Before;
@@ -83,7 +84,11 @@
         assertTrue(caps.hasDataAccess());
     }
 
-    @Test
+    // the property is deleted in U, on S & T getApfDrop8023Frames() is mainline and
+    // hardcoded to return true, so this simply verifies the unused property is also true,
+    // as modifying it would not take effect.  For S+ change NetworkStack's
+    // config_apfDrop802_3Frames instead.
+    @Test @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testGetApfDrop8023Frames() {
         // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly
         // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID.
@@ -105,7 +110,11 @@
                 ApfCapabilities.getApfDrop8023Frames());
     }
 
-    @Test
+    // the property is deleted in U, on S & T getApfEtherTypeBlackList() is mainline and
+    // hardcoded to return a specific default set of ethertypes, so this simply verifies
+    // that the unused property hasn't been changed away from the default, as it would
+    // not take effect.  For S+ change NetworkStack's config_apfEthTypeDenyList instead.
+    @Test @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testGetApfEtherTypeBlackList() {
         // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly
         // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID.
diff --git a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
index ab97f2d..b6e9b95 100644
--- a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
+++ b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
@@ -19,11 +19,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
-import static com.android.net.module.util.NetworkCapabilitiesUtils.unpackBits;
+import static com.android.net.module.util.BitUtils.unpackBits;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.net.ConnectivityMetricsEvent;
@@ -51,6 +51,7 @@
     private static final int FAKE_NET_ID = 100;
     private static final int[] FAKE_TRANSPORT_TYPES = unpackBits(TRANSPORT_WIFI);
     private static final long FAKE_TIME_STAMP = System.currentTimeMillis();
+    private static final long THREAD_TIMEOUT_MS = 10_000L;
     private static final String FAKE_INTERFACE_NAME = "test";
     private static final IpReachabilityEvent FAKE_EV =
             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
@@ -93,22 +94,26 @@
 
         final int nCallers = 10;
         final int nEvents = 10;
+        final Thread[] threads = new Thread[nCallers];
         for (int n = 0; n < nCallers; n++) {
             final int i = n;
-            new Thread() {
-                public void run() {
-                    for (int j = 0; j < nEvents; j++) {
-                        assertTrue(logger.log(makeExpectedEvent(
-                                FAKE_TIME_STAMP + i * 100 + j,
-                                FAKE_NET_ID + i * 100 + j,
-                                ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR,
-                                FAKE_INTERFACE_NAME)));
-                    }
+            threads[i] = new Thread(() -> {
+                for (int j = 0; j < nEvents; j++) {
+                    assertTrue(logger.log(makeExpectedEvent(
+                            FAKE_TIME_STAMP + i * 100 + j,
+                            FAKE_NET_ID + i * 100 + j,
+                            ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR,
+                            FAKE_INTERFACE_NAME)));
                 }
-            }.start();
+            });
+            threads[i].start();
+        }
+        // To ensure the events have been sent out on each thread. Wait for the thread to die.
+        for (Thread thread : threads) {
+            thread.join(THREAD_TIMEOUT_MS);
         }
 
-        List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
+        final List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents);
         Collections.sort(got, EVENT_COMPARATOR);
         Iterator<ConnectivityMetricsEvent> iter = got.iterator();
         for (int i = 0; i < nCallers; i++) {
@@ -123,17 +128,13 @@
         }
     }
 
-    private List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
+    private List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
         ArgumentCaptor<ConnectivityMetricsEvent> captor =
                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
-        verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture());
+        verify(mMockService, times(n)).logEvent(captor.capture());
         return captor.getAllValues();
     }
 
-    private List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
-        return verifyEvents(n, 10);
-    }
-
 
     private ConnectivityMetricsEvent makeExpectedEvent(long timestamp, int netId, long transports,
             String ifname) {
diff --git a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
index 192694b..cdf32a4 100644
--- a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
+++ b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
@@ -78,7 +78,7 @@
             NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
                     .setMeteredness(METERED_YES).build().let {
                         val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
-                                arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+                                arrayOf(TEST_IMSI1), emptyArray<String>(), METERED_YES,
                                 ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                                 OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
                         assertEquals(expectedTemplate, it)
@@ -91,7 +91,7 @@
             NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
                     .setRoaming(ROAMING_YES).setMeteredness(METERED_YES).build().let {
                         val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
-                                arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+                                arrayOf(TEST_IMSI1), emptyArray<String>(), METERED_YES,
                                 ROAMING_YES, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                                 OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
                         assertEquals(expectedTemplate, it)
@@ -107,7 +107,7 @@
         // regardless of IMSI. See buildTemplateMobileWildcard.
         NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build().let {
             val expectedTemplate = NetworkTemplate(MATCH_MOBILE_WILDCARD, null /*subscriberId*/,
-                    null /*subscriberIds*/, arrayOf<String>(),
+                    emptyArray<String>() /*subscriberIds*/, emptyArray<String>(),
                     METERED_YES, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                     OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
             assertEquals(expectedTemplate, it)
@@ -119,7 +119,7 @@
                 .setMeteredness(METERED_YES).setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
                 .build().let {
                     val expectedTemplate = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1,
-                            arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+                            arrayOf(TEST_IMSI1), emptyArray<String>(), METERED_YES,
                             ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS,
                             OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
                     assertEquals(expectedTemplate, it)
@@ -129,7 +129,7 @@
         // regardless of Wifi Network Key. See buildTemplateWifiWildcard and buildTemplateWifi.
         NetworkTemplate.Builder(MATCH_WIFI).build().let {
             val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
-                    null /*subscriberIds*/, arrayOf<String>(),
+                    emptyArray<String>() /*subscriberIds*/, emptyArray<String>(),
                     METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                     OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
             assertEquals(expectedTemplate, it)
@@ -139,7 +139,7 @@
         // See buildTemplateWifi(wifiNetworkKey).
         NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
             val expectedTemplate = NetworkTemplate(MATCH_WIFI, null /*subscriberId*/,
-                    null /*subscriberIds*/, arrayOf(TEST_WIFI_KEY1),
+                    emptyArray<String>() /*subscriberIds*/, arrayOf(TEST_WIFI_KEY1),
                     METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                     OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
             assertEquals(expectedTemplate, it)
@@ -161,7 +161,7 @@
         listOf(MATCH_ETHERNET, MATCH_BLUETOOTH).forEach { matchRule ->
             NetworkTemplate.Builder(matchRule).build().let {
                 val expectedTemplate = NetworkTemplate(matchRule, null /*subscriberId*/,
-                        null /*subscriberIds*/, arrayOf<String>(),
+                        emptyArray<String>() /*subscriberIds*/, emptyArray<String>(),
                         METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                         OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
                 assertEquals(expectedTemplate, it)
@@ -196,7 +196,7 @@
         // Verify template which matches wifi wildcard with the given empty key set.
         NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf<String>()).build().let {
             val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
-                    arrayOf<String>() /*subscriberIds*/, arrayOf<String>(),
+                    emptyArray<String>() /*subscriberIds*/, emptyArray<String>(),
                     METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                     OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
             assertEquals(expectedTemplate, it)
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 875b4a2..089d06f 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,3 +1,7 @@
 # Bug template url: http://b/new?component=31808
 set noparent
 file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
+
+# Only temporary ownership to improve ethernet code quality (b/236280707)
+# TODO: remove by 12/31/2022
+per-file net/src/android/net/cts/EthernetManagerTest.kt = prohr@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 5f032be..8c18a89 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -38,6 +38,7 @@
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
+import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_EXPORTED;
 import static com.android.testutils.Cleanup.testAndCleanup;
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
@@ -1550,8 +1551,9 @@
         final DownloadManager dm = context.getSystemService(DownloadManager.class);
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
+            final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
             context.registerReceiver(receiver,
-                    new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+                    new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), flags);
 
             // Enqueue a request and check only one download.
             final long id = dm.enqueue(new Request(
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index a6179fc..23cb15c 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -61,7 +61,9 @@
     // uncomment when b/13249961 is fixed
     // sdk_version: "current",
     platform_apis: true,
-    required: ["ConnectivityChecker"],
+    data: [":ConnectivityChecker"],
+    per_testcase_directory: true,
+    host_required: ["net-tests-utils-host-common"],
     test_config_template: "AndroidTestTemplate.xml",
 }
 
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 6b5bb93..25490da 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -35,6 +35,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
     <!-- TODO (b/186093901): remove after fixing resource querying -->
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.cpp b/tests/cts/net/jni/NativeMultinetworkJni.cpp
index 60e31bc..6610d10 100644
--- a/tests/cts/net/jni/NativeMultinetworkJni.cpp
+++ b/tests/cts/net/jni/NativeMultinetworkJni.cpp
@@ -40,8 +40,12 @@
 #define LOGD(fmt, ...) \
         __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__)
 
-#define EXPECT_GE(env, actual, expected, msg)                        \
+// Since the tests in this file commonly pass expression statements as parameters to these macros,
+// get the returned value of the statements to avoid statement double-called.
+#define EXPECT_GE(env, actual_stmt, expected_stmt, msg)              \
     do {                                                             \
+        const auto expected = (expected_stmt);                       \
+        const auto actual = (actual_stmt);                           \
         if (actual < expected) {                                     \
             jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
                     "%s:%d: %s EXPECT_GE: expected %d, got %d",      \
@@ -49,8 +53,10 @@
         }                                                            \
     } while (0)
 
-#define EXPECT_GT(env, actual, expected, msg)                        \
+#define EXPECT_GT(env, actual_stmt, expected_stmt, msg)              \
     do {                                                             \
+        const auto expected = (expected_stmt);                       \
+        const auto actual = (actual_stmt);                           \
         if (actual <= expected) {                                    \
             jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
                     "%s:%d: %s EXPECT_GT: expected %d, got %d",      \
@@ -58,8 +64,10 @@
         }                                                            \
     } while (0)
 
-#define EXPECT_EQ(env, expected, actual, msg)                        \
+#define EXPECT_EQ(env, expected_stmt, actual_stmt, msg)              \
     do {                                                             \
+        const auto expected = (expected_stmt);                       \
+        const auto actual = (actual_stmt);                           \
         if (actual != expected) {                                    \
             jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
                     "%s:%d: %s EXPECT_EQ: expected %d, got %d",      \
@@ -110,6 +118,18 @@
             // If there is no valid answer, test will fail.
             continue;
         }
+
+        const int rtype = ns_rr_type(rr);
+        if (family == AF_INET) {
+            // If there is no expected address type, test will fail.
+            if (rtype != ns_t_a) continue;
+        } else if (family == AF_INET6) {
+            // If there is no expected address type, test will fail.
+            if (rtype != ns_t_aaaa) continue;
+        } else {
+            return -EAFNOSUPPORT;
+        }
+
         const uint8_t* rdata = ns_rr_rdata(rr);
         char buffer[INET6_ADDRSTRLEN];
         if (inet_ntop(family, (const char*) rdata, buffer, sizeof(buffer)) == NULL) {
@@ -161,7 +181,7 @@
     // V6
     fd = android_res_nquery(handle, kHostname, ns_c_in, ns_t_aaaa, 0);
     EXPECT_GE(env, fd, 0, "v6 res_nquery");
-    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET6, ns_r_noerror),
             "v6 res_nquery check answers");
 }
 
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index 6b2a1ee..3c71c90 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -21,12 +21,14 @@
 
 import static androidx.test.InstrumentationRegistry.getContext;
 
+import static com.android.compatibility.common.util.BatteryUtils.hasBattery;
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -97,6 +99,7 @@
     @RequiresDevice // Virtual hardware does not support wifi battery stats
     public void testReportNetworkInterfaceForTransports() throws Exception {
         try {
+            assumeTrue("Battery is not present. Ignore test.", hasBattery());
             // Simulate the device being unplugged from charging.
             executeShellCommand("cmd battery unplug");
             executeShellCommand("cmd battery set status " + BATTERY_STATUS_DISCHARGING);
@@ -152,6 +155,13 @@
         // removing data activity tracking.
         mCtsNetUtils.ensureWifiConnected();
 
+        // There's rate limit to update mobile battery so if ConnectivityService calls
+        // BatteryStatsManager.reportMobileRadioPowerState when default network changed,
+        // the mobile stats might not be updated. But if the mobile update due to other
+        // reasons (plug/unplug, battery level change, etc) will be unaffected. Thus here
+        // dumps the battery stats to trigger a full sync of data.
+        executeShellCommand("dumpsys batterystats");
+
         // Check cellular battery stats are updated.
         runAsShell(UPDATE_DEVICE_STATS,
                 () -> assertStatsEventually(mBsm::getCellularBatteryStats,
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index aad8804..7c24c95 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -47,6 +47,7 @@
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.runner.AndroidJUnit4
 import com.android.modules.utils.build.SdkLevel.isAtLeastR
+import com.android.testutils.DeviceConfigRule
 import com.android.testutils.RecorderCallback
 import com.android.testutils.TestHttpServer
 import com.android.testutils.TestHttpServer.Request
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 7d1e13f..7662ba3 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -39,6 +39,7 @@
 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
 
 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.testutils.Cleanup.testAndCleanup;
 
@@ -70,11 +71,13 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
@@ -104,6 +107,8 @@
 @IgnoreUpTo(Build.VERSION_CODES.Q) // ConnectivityDiagnosticsManager did not exist in Q
 @AppModeFull(reason = "CHANGE_NETWORK_STATE, MANAGE_TEST_NETWORKS not grantable to instant apps")
 public class ConnectivityDiagnosticsManagerTest {
+    private static final String TAG = ConnectivityDiagnosticsManagerTest.class.getSimpleName();
+
     private static final int CALLBACK_TIMEOUT_MILLIS = 5000;
     private static final int NO_CALLBACK_INVOKED_TIMEOUT = 500;
     private static final long TIMESTAMP = 123456789L;
@@ -113,7 +118,7 @@
     private static final int UNKNOWN_DETECTION_METHOD = 4;
     private static final int FILTERED_UNKNOWN_DETECTION_METHOD = 0;
     private static final int CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT = 5000;
-    private static final int DELAY_FOR_ADMIN_UIDS_MILLIS = 5000;
+    private static final int DELAY_FOR_BROADCAST_IDLE = 30_000;
 
     private static final Executor INLINE_EXECUTOR = x -> x.run();
 
@@ -155,6 +160,23 @@
 
     private List<TestConnectivityDiagnosticsCallback> mRegisteredCallbacks;
 
+    private static void waitForBroadcastIdle(final long timeoutMs) throws InterruptedException {
+        final long st = SystemClock.elapsedRealtime();
+        // am wait-for-broadcast-idle will return immediately if the queue is already idle.
+        final Thread t = new Thread(() -> runShellCommand("am wait-for-broadcast-idle"));
+        t.start();
+        // Two notes about the case where join() times out :
+        // • It is fine to continue running the test. The broadcast queue might still be busy, but
+        //   there is no way as of now to wait for a particular broadcast to have been been
+        //   processed so it's possible the one the caller is interested in is in fact done,
+        //   making it worth running the rest of the test.
+        // • The thread will continue running its course in the test process. In this case it is
+        //   fine because the wait-for-broadcast-idle command doesn't have side effects, and the
+        //   thread does nothing else.
+        t.join(timeoutMs);
+        Log.i(TAG, "Waited for broadcast idle for " + (SystemClock.elapsedRealtime() - st) + "ms");
+    }
+
     @Before
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
@@ -283,10 +305,13 @@
         // broadcast. CPT then needs to update the corresponding DataConnection, which then
         // updates ConnectivityService. Unfortunately, this update to the NetworkCapabilities in
         // CS does not trigger NetworkCallback#onCapabilitiesChanged as changing the
-        // administratorUids is not a publicly visible change. In lieu of a better signal to
-        // deterministically wait for, use Thread#sleep here.
-        // TODO(b/157949581): replace this Thread#sleep with a deterministic signal
-        Thread.sleep(DELAY_FOR_ADMIN_UIDS_MILLIS);
+        // administratorUids is not a publicly visible change. Start by waiting for broadcast
+        // idle to make sure Telephony has received the carrier config change broadcast ; the
+        // delay to pass this information to CS is accounted in the delay in waiting for the
+        // callback.
+        waitForBroadcastIdle(DELAY_FOR_BROADCAST_IDLE);
+
+        Thread.sleep(5_000);
 
         // TODO(b/217559768): Receiving carrier config change and immediately checking carrier
         //  privileges is racy, as the CP status is updated after receiving the same signal. Move
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index cece4df..96acac3 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -165,7 +165,6 @@
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.VintfRuntimeInfo;
 import android.platform.test.annotations.AppModeFull;
@@ -193,6 +192,7 @@
 import com.android.testutils.ConnectivityModuleTest;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DeviceConfigRule;
 import com.android.testutils.DeviceInfoUtils;
 import com.android.testutils.DumpTestUtils;
 import com.android.testutils.RecorderCallback.CallbackEntry;
@@ -271,7 +271,10 @@
     private static final int MIN_KEEPALIVE_INTERVAL = 10;
 
     private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
-    private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 5_000;
+    // Timeout for waiting network to be validated. Set the timeout to 30s, which is more than
+    // DNS timeout.
+    // TODO(b/252972908): reset the original timer when aosp/2188755 is ramped up.
+    private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 30_000;
     private static final int NO_CALLBACK_TIMEOUT_MS = 100;
     private static final int SOCKET_TIMEOUT_MS = 100;
     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
@@ -353,7 +356,8 @@
         // Get com.android.internal.R.array.networkAttributes
         int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
         String[] naStrings = mContext.getResources().getStringArray(resId);
-        boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
+        boolean wifiOnly = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+                && !mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
         for (String naString : naStrings) {
             try {
                 final String[] splitConfig = naString.split(",");
@@ -556,6 +560,11 @@
         // got from other APIs.
         final Network[] networks = mCm.getAllNetworks();
         assertGreaterOrEqual(networks.length, 1);
+        final TestableNetworkCallback allNetworkLinkPropertiesListener =
+                new TestableNetworkCallback();
+        mCm.registerNetworkCallback(new NetworkRequest.Builder().clearCapabilities().build(),
+                allNetworkLinkPropertiesListener);
+
         final List<NetworkStateSnapshot> snapshots = runWithShellPermissionIdentity(
                 () -> mCm.getAllNetworkStateSnapshots(), NETWORK_SETTINGS);
         assertEquals(networks.length, snapshots.size());
@@ -581,7 +590,18 @@
             assertEquals("", caps.describeImmutableDifferences(
                     snapshot.getNetworkCapabilities()
                             .setNetworkSpecifier(redactedSnapshotCapSpecifier)));
-            assertEquals(mCm.getLinkProperties(network), snapshot.getLinkProperties());
+
+            // Don't check that the mutable fields are the same with synchronous calls, as
+            // the device may add or remove content of these fields in the middle of the test.
+            // Instead, search the target LinkProperties from received LinkPropertiesChanged
+            // callbacks. This is guaranteed to succeed because the callback is registered
+            // before getAllNetworkStateSnapshots is called.
+            final LinkProperties lpFromSnapshot = snapshot.getLinkProperties();
+            allNetworkLinkPropertiesListener.eventuallyExpect(CallbackEntry.LINK_PROPERTIES_CHANGED,
+                    NETWORK_CALLBACK_TIMEOUT_MS, 0 /* mark */, entry ->
+                            entry.getNetwork().equals(network)
+                                    && entry.getLp().equals(lpFromSnapshot));
+
             assertEquals(mCm.getNetworkInfo(network).getType(), snapshot.getLegacyType());
 
             if (network.equals(cellNetwork)) {
@@ -1044,6 +1064,8 @@
         final TestNetworkCallback bestMatchingCallback = new TestNetworkCallback();
         final Handler h = new Handler(Looper.getMainLooper());
         if (TestUtils.shouldTestSApis()) {
+            assertThrows(SecurityException.class, () ->
+                    registerSystemDefaultNetworkCallback(systemDefaultCallback, h));
             runWithShellPermissionIdentity(() -> {
                 registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
                 registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
@@ -1079,6 +1101,18 @@
         }
     }
 
+    @ConnectivityModuleTest
+    @IgnoreUpTo(Build.VERSION_CODES.R)
+    @Test
+    public void testRegisterSystemDefaultNetworkCallbackPermission() {
+        final Handler h = new Handler(Looper.getMainLooper());
+        // Verify registerSystemDefaultNetworkCallback can be accessed via
+        // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+        runWithShellPermissionIdentity(() ->
+                        registerSystemDefaultNetworkCallback(new TestNetworkCallback(), h),
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+    }
+
     /**
      * Tests both registerNetworkCallback and unregisterNetworkCallback similarly to
      * {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead
@@ -2348,8 +2382,9 @@
             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
         }
-        public void expectBlockedStatusCallback(Network network, int blockedStatus) {
-            super.expectBlockedStatusCallback(blockedStatus, network, NETWORK_CALLBACK_TIMEOUT_MS);
+        public void eventuallyExpectBlockedStatusCallback(Network network, int blockedStatus) {
+            super.eventuallyExpect(CallbackEntry.BLOCKED_STATUS_INT, NETWORK_CALLBACK_TIMEOUT_MS,
+                    (it) -> it.getNetwork().equals(network) && it.getBlocked() == blockedStatus);
         }
         public void onBlockedStatusChanged(Network network, int blockedReasons) {
             getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
@@ -2394,12 +2429,14 @@
         final Range<Integer> otherUidRange = new Range<>(otherUid, otherUid);
 
         setRequireVpnForUids(true, List.of(myUidRange));
-        myUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_LOCKDOWN_VPN);
+        myUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork,
+                BLOCKED_REASON_LOCKDOWN_VPN);
         otherUidCallback.assertNoBlockedStatusCallback();
 
         setRequireVpnForUids(true, List.of(myUidRange, otherUidRange));
         myUidCallback.assertNoBlockedStatusCallback();
-        otherUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_LOCKDOWN_VPN);
+        otherUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork,
+                BLOCKED_REASON_LOCKDOWN_VPN);
 
         // setRequireVpnForUids does no deduplication or refcounting. Removing myUidRange does not
         // unblock myUid because it was added to the blocked ranges twice.
@@ -2408,8 +2445,8 @@
         otherUidCallback.assertNoBlockedStatusCallback();
 
         setRequireVpnForUids(false, List.of(myUidRange, otherUidRange));
-        myUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
-        otherUidCallback.expectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
+        myUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
+        otherUidCallback.eventuallyExpectBlockedStatusCallback(defaultNetwork, BLOCKED_REASON_NONE);
 
         myUidCallback.assertNoBlockedStatusCallback();
         otherUidCallback.assertNoBlockedStatusCallback();
@@ -2618,8 +2655,7 @@
             // the network with the TEST transport. Also wait for validation here, in case there
             // is a bug that's only visible when the network is validated.
             setWifiMeteredStatusAndWait(ssid, true /* isMetered */, true /* waitForValidation */);
-            defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork,
-                    NETWORK_CALLBACK_TIMEOUT_MS);
+            defaultCallback.expect(CallbackEntry.LOST, wifiNetwork, NETWORK_CALLBACK_TIMEOUT_MS);
             waitForAvailable(defaultCallback, tnt.getNetwork());
             // Depending on if this device has cellular connectivity or not, multiple available
             // callbacks may be received. Eventually, metered Wi-Fi should be the final available
@@ -2628,10 +2664,9 @@
             waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
         }, /* cleanup */ () -> {
             // Validate that removing the test network will fallback to the default network.
-            runWithShellPermissionIdentity(tnt::teardown);
-            defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
-                    NETWORK_CALLBACK_TIMEOUT_MS);
-            waitForAvailable(defaultCallback);
+                runWithShellPermissionIdentity(tnt::teardown);
+                defaultCallback.expect(CallbackEntry.LOST, tnt, NETWORK_CALLBACK_TIMEOUT_MS);
+                waitForAvailable(defaultCallback);
             }, /* cleanup */ () -> {
                 setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
             }, /* cleanup */ () -> {
@@ -2666,8 +2701,7 @@
             waitForAvailable(systemDefaultCallback, wifiNetwork);
         }, /* cleanup */ () -> {
                 runWithShellPermissionIdentity(tnt::teardown);
-                defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
-                        NETWORK_CALLBACK_TIMEOUT_MS);
+                defaultCallback.expect(CallbackEntry.LOST, tnt, NETWORK_CALLBACK_TIMEOUT_MS);
 
                 // This network preference should only ever use the test network therefore available
                 // should not trigger when the test network goes down (e.g. switch to cellular).
@@ -2751,6 +2785,27 @@
                 mCm.getActiveNetwork(), false /* accept */ , false /* always */));
     }
 
+    private void ensureCellIsValidatedBeforeMockingValidationUrls() {
+        // Verify that current supported network is validated so that the mock http server will not
+        // apply to unexpected networks. Also see aosp/2208680.
+        //
+        // This may also apply to wifi in principle, but in practice methods that mock validation
+        // URL all disconnect wifi forcefully anyway, so don't wait for wifi to validate.
+        if (mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
+            ensureValidatedNetwork(makeCellNetworkRequest());
+        }
+    }
+
+    private void ensureValidatedNetwork(NetworkRequest request) {
+        final TestableNetworkCallback cb = new TestableNetworkCallback();
+        mCm.registerNetworkCallback(request, cb);
+        cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                NETWORK_CALLBACK_TIMEOUT_MS,
+                entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+                        .hasCapability(NET_CAPABILITY_VALIDATED));
+        mCm.unregisterNetworkCallback(cb);
+    }
+
     @AppModeFull(reason = "WRITE_DEVICE_CONFIG permission can't be granted to instant apps")
     @Test
     public void testAcceptPartialConnectivity_validatedNetwork() throws Exception {
@@ -2801,7 +2856,7 @@
             // Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
             // apply here. Thus, turn off wifi first and restart to restore.
             mTestValidationConfigRule.runAfterNextCleanup(() -> {
-                runShellCommand("svc wifi disable");
+                mCtsNetUtils.disableWifi();
                 mCtsNetUtils.ensureWifiConnected();
             });
         }
@@ -2843,7 +2898,7 @@
             /// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
             // apply here. Thus, turn off wifi first and restart to restore.
             mTestValidationConfigRule.runAfterNextCleanup(() -> {
-                runShellCommand("svc wifi disable");
+                mCtsNetUtils.disableWifi();
                 mCtsNetUtils.ensureWifiConnected();
             });
         }
@@ -2882,7 +2937,8 @@
             assertTrue(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
                     NET_CAPABILITY_VALIDATED));
 
-            // Configure response code for unvalidated network
+            // The cell network has already been checked to be validated.
+            // Configure response code for unvalidated network.
             configTestServer(Status.INTERNAL_ERROR, Status.INTERNAL_ERROR);
             mCm.reportNetworkConnectivity(wifiNetwork, false);
             // Default network should stay on unvalidated wifi because avoid bad wifi is disabled.
@@ -2970,6 +3026,8 @@
     }
 
     private Network prepareValidatedNetwork() throws Exception {
+        ensureCellIsValidatedBeforeMockingValidationUrls();
+
         prepareHttpServer();
         configTestServer(Status.NO_CONTENT, Status.NO_CONTENT);
         // Disconnect wifi first then start wifi network with configuration.
@@ -2980,6 +3038,8 @@
     }
 
     private Network preparePartialConnectivity() throws Exception {
+        ensureCellIsValidatedBeforeMockingValidationUrls();
+
         prepareHttpServer();
         // Configure response code for partial connectivity
         configTestServer(Status.INTERNAL_ERROR  /* httpsStatusCode */,
@@ -2993,6 +3053,8 @@
     }
 
     private Network prepareUnvalidatedNetwork() throws Exception {
+        ensureCellIsValidatedBeforeMockingValidationUrls();
+
         prepareHttpServer();
         // Configure response code for unvalidated network
         configTestServer(Status.INTERNAL_ERROR /* httpsStatusCode */,
diff --git a/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt b/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt
deleted file mode 100644
index 3a36cee..0000000
--- a/tests/cts/net/src/android/net/cts/DeviceConfigRule.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2022 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 android.net.cts
-
-import android.Manifest.permission.READ_DEVICE_CONFIG
-import android.Manifest.permission.WRITE_DEVICE_CONFIG
-import android.provider.DeviceConfig
-import android.util.Log
-import com.android.modules.utils.build.SdkLevel
-import com.android.testutils.FunctionalUtils.ThrowingRunnable
-import com.android.testutils.runAsShell
-import com.android.testutils.tryTest
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.Executor
-import java.util.concurrent.TimeUnit
-
-private val TAG = DeviceConfigRule::class.simpleName
-
-/**
- * A [TestRule] that helps set [DeviceConfig] for tests and clean up the test configuration
- * automatically on teardown.
- *
- * The rule can also optionally retry tests when they fail following an external change of
- * DeviceConfig before S; this typically happens because device config flags are synced while the
- * test is running, and DisableConfigSyncTargetPreparer is only usable starting from S.
- *
- * @param retryCountBeforeSIfConfigChanged if > 0, when the test fails before S, check if
- *        the configs that were set through this rule were changed, and retry the test
- *        up to the specified number of times if yes.
- */
-class DeviceConfigRule @JvmOverloads constructor(
-    val retryCountBeforeSIfConfigChanged: Int = 0
-) : TestRule {
-    // Maps (namespace, key) -> value
-    private val originalConfig = mutableMapOf<Pair<String, String>, String?>()
-    private val usedConfig = mutableMapOf<Pair<String, String>, String?>()
-
-    /**
-     * Actions to be run after cleanup of the config, for the current test only.
-     */
-    private val currentTestCleanupActions = mutableListOf<ThrowingRunnable>()
-
-    override fun apply(base: Statement, description: Description): Statement {
-        return TestValidationUrlStatement(base, description)
-    }
-
-    private inner class TestValidationUrlStatement(
-        private val base: Statement,
-        private val description: Description
-    ) : Statement() {
-        override fun evaluate() {
-            var retryCount = if (SdkLevel.isAtLeastS()) 1 else retryCountBeforeSIfConfigChanged + 1
-            while (retryCount > 0) {
-                retryCount--
-                tryTest {
-                    base.evaluate()
-                    // Can't use break/return out of a loop here because this is a tryTest lambda,
-                    // so set retryCount to exit instead
-                    retryCount = 0
-                }.catch<Throwable> { e -> // junit AssertionFailedError does not extend Exception
-                    if (retryCount == 0) throw e
-                    usedConfig.forEach { (key, value) ->
-                        val currentValue = runAsShell(READ_DEVICE_CONFIG) {
-                            DeviceConfig.getProperty(key.first, key.second)
-                        }
-                        if (currentValue != value) {
-                            Log.w(TAG, "Test failed with unexpected device config change, retrying")
-                            return@catch
-                        }
-                    }
-                    throw e
-                } cleanupStep {
-                    runAsShell(WRITE_DEVICE_CONFIG) {
-                        originalConfig.forEach { (key, value) ->
-                            DeviceConfig.setProperty(
-                                    key.first, key.second, value, false /* makeDefault */)
-                        }
-                    }
-                } cleanupStep {
-                    originalConfig.clear()
-                    usedConfig.clear()
-                } cleanup {
-                    // Fold all cleanup actions into cleanup steps of an empty tryTest, so they are
-                    // all run even if exceptions are thrown, and exceptions are reported properly.
-                    currentTestCleanupActions.fold(tryTest { }) {
-                        tryBlock, action -> tryBlock.cleanupStep { action.run() }
-                    }.cleanup {
-                        currentTestCleanupActions.clear()
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Set a configuration key/value. After the test case ends, it will be restored to the value it
-     * had when this method was first called.
-     */
-    fun setConfig(namespace: String, key: String, value: String?): String? {
-        Log.i(TAG, "Setting config \"$key\" to \"$value\"")
-        val readWritePermissions = arrayOf(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG)
-
-        val keyPair = Pair(namespace, key)
-        val existingValue = runAsShell(*readWritePermissions) {
-            DeviceConfig.getProperty(namespace, key)
-        }
-        if (!originalConfig.containsKey(keyPair)) {
-            originalConfig[keyPair] = existingValue
-        }
-        usedConfig[keyPair] = value
-        if (existingValue == value) {
-            // Already the correct value. There may be a race if a change is already in flight,
-            // but if multiple threads update the config there is no way to fix that anyway.
-            Log.i(TAG, "\"$key\" already had value \"$value\"")
-            return value
-        }
-
-        val future = CompletableFuture<String>()
-        val listener = DeviceConfig.OnPropertiesChangedListener {
-            // The listener receives updates for any change to any key, so don't react to
-            // changes that do not affect the relevant key
-            if (!it.keyset.contains(key)) return@OnPropertiesChangedListener
-            // "null" means absent in DeviceConfig : there is no such thing as a present but
-            // null value, so the following works even if |value| is null.
-            if (it.getString(key, null) == value) {
-                future.complete(value)
-            }
-        }
-
-        return tryTest {
-            runAsShell(*readWritePermissions) {
-                DeviceConfig.addOnPropertiesChangedListener(
-                        DeviceConfig.NAMESPACE_CONNECTIVITY,
-                        inlineExecutor,
-                        listener)
-                DeviceConfig.setProperty(
-                        DeviceConfig.NAMESPACE_CONNECTIVITY,
-                        key,
-                        value,
-                        false /* makeDefault */)
-                // Don't drop the permission until the config is applied, just in case
-                future.get(NetworkValidationTestUtil.TIMEOUT_MS, TimeUnit.MILLISECONDS)
-            }.also {
-                Log.i(TAG, "Config \"$key\" successfully set to \"$value\"")
-            }
-        } cleanup {
-            DeviceConfig.removeOnPropertiesChangedListener(listener)
-        }
-    }
-
-    private val inlineExecutor get() = Executor { r -> r.run() }
-
-    /**
-     * Add an action to be run after config cleanup when the current test case ends.
-     */
-    fun runAfterNextCleanup(action: ThrowingRunnable) {
-        currentTestCleanupActions.add(action)
-    }
-}
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 0c53411..3821cea 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -200,13 +200,13 @@
             super(data);
 
             // Check QR field.(query (0), or a response (1)).
-            if ((mHeader.flags & (1 << 15)) == 0) {
+            if ((mHeader.getFlags() & (1 << 15)) == 0) {
                 throw new DnsParseException("Not an answer packet");
             }
         }
 
         int getRcode() {
-            return mHeader.rcode;
+            return mHeader.getFlags() & 0x0F;
         }
 
         int getANCount() {
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index 8940075..db13c49 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -105,6 +105,7 @@
 
 private const val TAG = "DscpPolicyTest"
 private const val PACKET_TIMEOUT_MS = 2_000L
+private const val IPV6_ADDRESS_WAIT_TIME_MS = 10_000L
 
 @AppModeFull(reason = "Instant apps cannot create test networks")
 @RunWith(AndroidJUnit4::class)
@@ -222,7 +223,7 @@
         var inet6Addr: Inet6Address? = null
         val onLinkPrefix = raResponder.prefix
         val startTime = SystemClock.elapsedRealtime()
-        while (SystemClock.elapsedRealtime() - startTime < PACKET_TIMEOUT_MS) {
+        while (SystemClock.elapsedRealtime() - startTime < IPV6_ADDRESS_WAIT_TIME_MS) {
             SystemClock.sleep(50 /* ms */)
             val sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
             try {
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index b21c5b4..7e91478 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -41,6 +41,8 @@
 import android.net.MacAddress
 import android.net.Network
 import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
 import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
@@ -55,8 +57,9 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.OutcomeReceiver
+import android.os.SystemProperties
+import android.os.Process
 import android.platform.test.annotations.AppModeFull
-import android.util.ArraySet
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.net.module.util.TrackRecord
@@ -66,22 +69,24 @@
 import com.android.testutils.DeviceInfoUtils.isKernelVersionAtLeast
 import com.android.testutils.RecorderCallback.CallbackEntry.Available
 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
+import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
 import com.android.testutils.RouterAdvertisementResponder
-import com.android.testutils.SkipPresubmit
 import com.android.testutils.TapPacketReader
 import com.android.testutils.TestableNetworkCallback
-import com.android.testutils.anyNetwork
+import com.android.testutils.assertThrows
 import com.android.testutils.runAsShell
 import com.android.testutils.waitForIdle
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.io.IOException
 import java.net.Inet6Address
 import java.util.Random
+import java.net.Socket
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
@@ -96,11 +101,14 @@
 import kotlin.test.fail
 
 private const val TAG = "EthernetManagerTest"
-private const val TIMEOUT_MS = 1000L
+// This timeout does not affect the test duration for passing tests. It needs to be long enough to
+// account for RS delay (and potentially the first retry interval (4s)). There have been failures
+// where the interface did not gain provisioning within the allotted timeout.
+private const val TIMEOUT_MS = 10_000L
 // Timeout used to confirm no callbacks matching given criteria are received. Must be long enough to
 // process all callbacks including ip provisioning when using the updateConfiguration API.
 // Note that increasing this timeout increases the test duration.
-private const val NO_CALLBACK_TIMEOUT_MS = 200L
+private const val NO_CALLBACK_TIMEOUT_MS = 500L
 
 private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP,
         IpConfiguration.ProxySettings.NONE, null, null)
@@ -109,6 +117,10 @@
         .addTransportType(TRANSPORT_ETHERNET)
         .removeCapability(NET_CAPABILITY_TRUSTED)
         .build()
+private val TEST_CAPS = NetworkCapabilities.Builder(ETH_REQUEST.networkCapabilities)
+        .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+        .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+        .build()
 private val STATIC_IP_CONFIGURATION = IpConfiguration.Builder()
         .setStaticIpConfiguration(StaticIpConfiguration.Builder()
                 .setIpAddress(LinkAddress("192.0.2.1/30")).build())
@@ -119,7 +131,6 @@
 @RunWith(DevSdkIgnoreRunner::class)
 // This test depends on behavior introduced post-T as part of connectivity module updates
 @ConnectivityModuleTest
-@SkipPresubmit(reason = "Flaky: b/240323229; remove annotation after fixing")
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
 class EthernetManagerTest {
 
@@ -145,15 +156,18 @@
         private val raResponder: RouterAdvertisementResponder
         private val tnm: TestNetworkManager
         val name get() = tapInterface.interfaceName
+        val onLinkPrefix get() = raResponder.prefix
 
         init {
             tnm = runAsShell(MANAGE_TEST_NETWORKS) {
                 context.getSystemService(TestNetworkManager::class.java)
             }
             tapInterface = runAsShell(MANAGE_TEST_NETWORKS) {
-                // setting RS delay to 0 and disabling DAD speeds up tests.
-                tnm.createTapInterface(hasCarrier, false /* bringUp */,
-                        true /* disableIpv6ProvisioningDelay */)
+                // Configuring a tun/tap interface always enables the carrier. If hasCarrier is
+                // false, it is subsequently disabled. This means that the interface may briefly get
+                // link. With IPv6 provisioning delays (RS delay and DAD) disabled, this can cause
+                // tests that expect no network to come up when hasCarrier is false to become flaky.
+                tnm.createTapInterface(hasCarrier, false /* bringUp */)
             }
             val mtu = tapInterface.mtu
             packetReader = TapPacketReader(handler, tapInterface.fileDescriptor.fileDescriptor, mtu)
@@ -240,7 +254,7 @@
         }
 
         fun <T : CallbackEntry> expectCallback(expected: T): T {
-            val event = pollForNextCallback()
+            val event = pollOrThrow()
             assertEquals(expected, event)
             return event as T
         }
@@ -257,7 +271,7 @@
                 InterfaceStateChanged(iface, state, role,
                         if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null)
 
-        fun pollForNextCallback(): CallbackEntry {
+        fun pollOrThrow(): CallbackEntry {
             return events.poll(TIMEOUT_MS) ?: fail("Did not receive callback after ${TIMEOUT_MS}ms")
         }
 
@@ -290,8 +304,8 @@
             available.completeExceptionally(IllegalStateException("onUnavailable was called"))
         }
 
-        fun expectOnAvailable(): String {
-            return available.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+        fun expectOnAvailable(timeout: Long = TIMEOUT_MS): String {
+            return available.get(timeout, TimeUnit.MILLISECONDS)
         }
 
         fun expectOnUnavailable() {
@@ -312,10 +326,12 @@
         private val result = CompletableFuture<String>()
 
         override fun onResult(iface: String) {
+            assertFalse(result.isDone())
             result.complete(iface)
         }
 
         override fun onError(e: EthernetNetworkManagementException) {
+            assertFalse(result.isDone())
             result.completeExceptionally(e)
         }
 
@@ -366,7 +382,10 @@
         setIncludeTestInterfaces(false)
 
         for (listener in addedListeners) {
+            // Even if a given listener was not registered as both an interface and ethernet state
+            // listener, calling remove is safe.
             em.removeInterfaceStateListener(listener)
+            em.removeEthernetStateListener(listener)
         }
         registeredCallbacks.forEach { cm.unregisterNetworkCallback(it) }
         releaseTetheredInterface()
@@ -375,10 +394,25 @@
     // Setting the carrier up / down relies on TUNSETCARRIER which was added in kernel version 5.0.
     private fun assumeChangingCarrierSupported() = assumeTrue(isKernelVersionAtLeast("5.0.0"))
 
+    private fun isAdbOverEthernet(): Boolean {
+        // If no ethernet interface is available, adb is not connected over ethernet.
+        if (em.getInterfaceList().isEmpty()) return false
+
+        // cuttlefish is special and does not connect adb over ethernet.
+        if (SystemProperties.get("ro.product.board", "") == "cutf") return false
+
+        // Check if adb is connected over the network.
+        return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 ||
+                SystemProperties.getInt("service.adb.tcp.port", -1) > -1)
+    }
+
     private fun addInterfaceStateListener(listener: EthernetStateListener) {
-        runAsShell(CONNECTIVITY_USE_RESTRICTED_NETWORKS) {
-            em.addInterfaceStateListener(handler::post, listener)
-        }
+        em.addInterfaceStateListener(handler::post, listener)
+        addedListeners.add(listener)
+    }
+
+    private fun addEthernetStateListener(listener: EthernetStateListener) {
+        em.addEthernetStateListener(handler::post, listener)
         addedListeners.add(listener)
     }
 
@@ -412,14 +446,18 @@
     }
 
     private fun requestNetwork(request: NetworkRequest): TestableNetworkCallback {
-        return TestableNetworkCallback().also {
+        return TestableNetworkCallback(
+                timeoutMs = TIMEOUT_MS,
+                noCallbackTimeoutMs = NO_CALLBACK_TIMEOUT_MS).also {
             cm.requestNetwork(request, it)
             registeredCallbacks.add(it)
         }
     }
 
     private fun registerNetworkListener(request: NetworkRequest): TestableNetworkCallback {
-        return TestableNetworkCallback().also {
+        return TestableNetworkCallback(
+                timeoutMs = TIMEOUT_MS,
+                noCallbackTimeoutMs = NO_CALLBACK_TIMEOUT_MS).also {
             cm.registerNetworkCallback(request, it)
             registeredCallbacks.add(it)
         }
@@ -471,69 +509,71 @@
         }
     }
 
+    // WARNING: check that isAdbOverEthernet() is false before calling setEthernetEnabled(false).
     private fun setEthernetEnabled(enabled: Boolean) {
         runAsShell(NETWORK_SETTINGS) { em.setEthernetEnabled(enabled) }
 
         val listener = EthernetStateListener()
-        em.addEthernetStateListener(handler::post, listener)
-        try {
-            listener.eventuallyExpect(
-                    if (enabled) ETHERNET_STATE_ENABLED else ETHERNET_STATE_DISABLED)
-        } finally {
-            em.removeEthernetStateListener(listener)
-        }
+        addEthernetStateListener(listener)
+        listener.eventuallyExpect(if (enabled) ETHERNET_STATE_ENABLED else ETHERNET_STATE_DISABLED)
     }
 
     // NetworkRequest.Builder does not create a copy of the passed NetworkRequest, so in order to
     // keep ETH_REQUEST as it is, a defensive copy is created here.
-    private fun NetworkRequest.createCopyWithEthernetSpecifier(ifaceName: String) =
+    private fun NetworkRequest.copyWithEthernetSpecifier(ifaceName: String) =
         NetworkRequest.Builder(NetworkRequest(ETH_REQUEST))
             .setNetworkSpecifier(EthernetNetworkSpecifier(ifaceName)).build()
 
-    // It can take multiple seconds for the network to become available.
-    private fun TestableNetworkCallback.expectAvailable() =
-            expectCallback<Available>(anyNetwork(), 5000 /* ms timeout */).network
-
-    private fun TestableNetworkCallback.expectLost(n: Network = anyNetwork()) =
-            expectCallback<Lost>(n, 5000 /* ms timeout */)
-
     // b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use
     // eventuallyExpect(Lost::class) instead.
     private fun TestableNetworkCallback.eventuallyExpectLost(n: Network? = null) =
-        eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true }
+        eventuallyExpect(Lost::class) { n?.equals(it.network) ?: true }
 
     private fun TestableNetworkCallback.assertNeverLost(n: Network? = null) =
-        assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS) {
+        assertNoCallbackThat() {
             it is Lost && (n?.equals(it.network) ?: true)
         }
 
     private fun TestableNetworkCallback.assertNeverAvailable(n: Network? = null) =
-        assertNoCallbackThat() { it is Available && (n?.equals(it.network) ?: true) }
+        assertNoCallbackThat { it is Available && (n?.equals(it.network) ?: true) }
 
     private fun TestableNetworkCallback.expectCapabilitiesWithInterfaceName(name: String) =
-        expectCapabilitiesThat(anyNetwork()) {
-            it.networkSpecifier == EthernetNetworkSpecifier(name)
-        }
+        expect<CapabilitiesChanged> { it.caps.networkSpecifier == EthernetNetworkSpecifier(name) }
 
-    private fun TestableNetworkCallback.expectCapabilitiesWithCapability(cap: Int) =
-        expectCapabilitiesThat(anyNetwork(), TIMEOUT_MS) {
-            it.hasCapability(cap)
+    private fun TestableNetworkCallback.eventuallyExpectCapabilities(nc: NetworkCapabilities) {
+        // b/233534110: eventuallyExpect<CapabilitiesChanged>() does not advance ReadHead.
+        eventuallyExpect(CapabilitiesChanged::class) {
+            // CS may mix in additional capabilities, so NetworkCapabilities#equals cannot be used.
+            // Check if all expected capabilities are present instead.
+            it is CapabilitiesChanged && nc.capabilities.all { c -> it.caps.hasCapability(c) }
         }
+    }
 
-    private fun TestableNetworkCallback.expectLinkPropertiesWithLinkAddress(addr: LinkAddress) =
-        expectLinkPropertiesThat(anyNetwork(), TIMEOUT_MS) {
-            // LinkAddress.equals isn't possible as the system changes the LinkAddress.flags value.
-            // any() must be used since the interface may also have a link-local address.
-            it.linkAddresses.any { x -> x.isSameAddressAs(addr) }
+    private fun TestableNetworkCallback.eventuallyExpectLpForStaticConfig(
+        config: StaticIpConfiguration
+    ) {
+        // b/233534110: eventuallyExpect<LinkPropertiesChanged>() does not advance ReadHead.
+        eventuallyExpect(LinkPropertiesChanged::class) {
+            it is LinkPropertiesChanged && it.lp.linkAddresses.any { la ->
+                la.isSameAddressAs(config.ipAddress)
+            }
         }
+    }
 
     @Test
     fun testCallbacks() {
+        // Only run this test when no non-restricted / physical interfaces are present.
+        // This test ensures that interface state listeners function properly, so the assumption
+        // check is explicitly *not* using an interface state listener.
+        // Since restricted interfaces cannot be used for tethering,
+        // assumeNoInterfaceForTetheringAvailable() is an okay proxy.
+        assumeNoInterfaceForTetheringAvailable()
+
         // If an interface exists when the callback is registered, it is reported on registration.
         val iface = createInterface()
         val listener1 = EthernetStateListener()
         addInterfaceStateListener(listener1)
-        validateListenerOnRegistration(listener1)
+        listener1.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
 
         // If an interface appears, existing callbacks see it.
         val iface2 = createInterface()
@@ -543,7 +583,8 @@
         // Register a new listener, it should see state of all existing interfaces immediately.
         val listener2 = EthernetStateListener()
         addInterfaceStateListener(listener2)
-        validateListenerOnRegistration(listener2)
+        listener2.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+        listener2.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
 
         // Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE.
         removeInterface(iface)
@@ -560,12 +601,32 @@
         }
     }
 
+    @Test
+    fun testCallbacks_withRunningInterface() {
+        assumeFalse(isAdbOverEthernet())
+        // Only run this test when no non-restricted / physical interfaces are present.
+        assumeNoInterfaceForTetheringAvailable()
+
+        val iface = createInterface()
+        val listener = EthernetStateListener()
+        addInterfaceStateListener(listener)
+        listener.eventuallyExpect(iface, STATE_LINK_UP, ROLE_CLIENT)
+
+        // Remove running interface. The interface stays running but is no longer tracked.
+        setEthernetEnabled(false)
+        listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE)
+
+        setEthernetEnabled(true)
+        listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+        listener.assertNoCallback()
+    }
+
     private fun assumeNoInterfaceForTetheringAvailable() {
         // Interfaces that have configured NetworkCapabilities will never be used for tethering,
         // see aosp/2123900.
         try {
             // assumeException does not exist.
-            requestTetheredInterface().expectOnAvailable()
+            requestTetheredInterface().expectOnAvailable(NO_CALLBACK_TIMEOUT_MS)
             // interface used for tethering is available, throw an assumption error.
             assumeTrue(false)
         } catch (e: TimeoutException) {
@@ -601,28 +662,6 @@
         listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
     }
 
-    /**
-     * Validate all interfaces are returned for an EthernetStateListener upon registration.
-     */
-    private fun validateListenerOnRegistration(listener: EthernetStateListener) {
-        // Get all tracked interfaces to validate on listener registration. Ordering and interface
-        // state (up/down) can't be validated for interfaces not created as part of testing.
-        val ifaces = em.getInterfaceList()
-        val polledIfaces = ArraySet<String>()
-        for (i in ifaces) {
-            val event = (listener.pollForNextCallback() as InterfaceStateChanged)
-            val iface = event.iface
-            assertTrue(polledIfaces.add(iface), "Duplicate interface $iface returned")
-            assertTrue(ifaces.contains(iface), "Untracked interface $iface returned")
-            // If the event's iface was created in the test, additional criteria can be validated.
-            createdIfaces.find { it.name.equals(iface) }?.let {
-                assertEquals(event, listener.createChangeEvent(it.name, STATE_LINK_UP, ROLE_CLIENT))
-            }
-        }
-        // Assert all callbacks are accounted for.
-        listener.assertNoCallback()
-    }
-
     @Test
     fun testGetInterfaceList() {
         // Create two test interfaces and check the return list contains the interface names.
@@ -649,9 +688,11 @@
 
         // install a listener which will later be used to verify the Lost callback
         val listenerCb = registerNetworkListener(ETH_REQUEST)
+        // assert the network is only brought up by a request.
+        listenerCb.assertNeverAvailable()
 
         val cb = requestNetwork(ETH_REQUEST)
-        val network = cb.expectAvailable()
+        val network = cb.expect<Available>().network
 
         cb.assertNeverLost()
         releaseRequest(cb)
@@ -668,7 +709,7 @@
         // createInterface and the interface actually being properly registered with the ethernet
         // module, so it is extremely unlikely that the CS handler thread has not run until then.
         val iface = createInterface()
-        val network = cb.expectAvailable()
+        val network = cb.expect<Available>().network
 
         // remove interface before network request has been removed
         cb.assertNeverLost()
@@ -681,9 +722,9 @@
         val iface1 = createInterface()
         val iface2 = createInterface()
 
-        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.name))
+        val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface2.name))
 
-        val network = cb.expectAvailable()
+        cb.expect<Available>()
         cb.expectCapabilitiesWithInterfaceName(iface2.name)
 
         removeInterface(iface1)
@@ -697,7 +738,7 @@
         val iface1 = createInterface()
 
         val cb = requestNetwork(ETH_REQUEST)
-        val network = cb.expectAvailable()
+        val network = cb.expect<Available>().network
 
         // create another network and verify the request sticks to the current network
         val iface2 = createInterface()
@@ -706,7 +747,7 @@
         // remove iface1 and verify the request brings up iface2
         removeInterface(iface1)
         cb.eventuallyExpectLost(network)
-        val network2 = cb.expectAvailable()
+        cb.expect<Available>()
     }
 
     @Test
@@ -714,16 +755,16 @@
         val iface1 = createInterface()
         val iface2 = createInterface()
 
-        val cb1 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface1.name))
-        val cb2 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.name))
+        val cb1 = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface1.name))
+        val cb2 = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface2.name))
         val cb3 = requestNetwork(ETH_REQUEST)
 
-        cb1.expectAvailable()
+        cb1.expect<Available>()
         cb1.expectCapabilitiesWithInterfaceName(iface1.name)
-        cb2.expectAvailable()
+        cb2.expect<Available>()
         cb2.expectCapabilitiesWithInterfaceName(iface2.name)
         // this request can be matched by either network.
-        cb3.expectAvailable()
+        cb3.expect<Available>()
 
         cb1.assertNeverLost()
         cb2.assertNeverLost()
@@ -738,10 +779,10 @@
         val cb1 = requestNetwork(ETH_REQUEST)
 
         val iface = createInterface()
-        val network = cb1.expectAvailable()
+        val network = cb1.expect<Available>().network
 
         val cb2 = requestNetwork(ETH_REQUEST)
-        cb2.expectAvailable()
+        cb2.expect<Available>()
 
         // release the first request; this used to trigger b/197548738
         releaseRequest(cb1)
@@ -758,15 +799,31 @@
         val iface = createInterface(false /* hasCarrier */)
 
         val cb = requestNetwork(ETH_REQUEST)
-        cb.assertNeverAvailable()
+        // TUNSETCARRIER races with the bring up code, so the network *can* become available despite
+        // it being "created with no carrier".
+        // TODO(b/249611919): re-enable assertion once kernel supports IFF_NO_CARRIER.
+        // cb.assertNeverAvailable()
 
         iface.setCarrierEnabled(true)
-        cb.expectAvailable()
+        cb.expect<Available>()
 
         iface.setCarrierEnabled(false)
         cb.eventuallyExpectLost()
     }
 
+    // TODO: move to MTS
+    @Test
+    fun testNetworkRequest_linkPropertiesUpdate() {
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST)
+        // b/233534110: eventuallyExpect<LinkPropertiesChanged>() does not advance ReadHead
+        cb.eventuallyExpect(LinkPropertiesChanged::class) {
+            it is LinkPropertiesChanged && it.lp.addresses.any {
+                address -> iface.onLinkPrefix.contains(address)
+            }
+        }
+    }
+
     @Test
     fun testRemoveInterface_whileInServerMode() {
         assumeNoInterfaceForTetheringAvailable()
@@ -793,78 +850,142 @@
     fun testEnableDisableInterface_withActiveRequest() {
         val iface = createInterface()
         val cb = requestNetwork(ETH_REQUEST)
-        cb.expectAvailable()
+        cb.expect<Available>()
         cb.assertNeverLost()
 
         disableInterface(iface).expectResult(iface.name)
         cb.eventuallyExpectLost()
 
         enableInterface(iface).expectResult(iface.name)
-        cb.expectAvailable()
+        cb.expect<Available>()
+    }
+
+    @Test
+    fun testEnableDisableInterface_withoutStateChange() {
+        val iface = createInterface()
+        // Interface is already enabled, so enableInterface() should return success
+        enableInterface(iface).expectResult(iface.name)
+
+        disableInterface(iface).expectResult(iface.name)
+        // Interface is already disabled, so disableInterface() should return success.
+        disableInterface(iface).expectResult(iface.name)
+    }
+
+    @Test
+    fun testEnableDisableInterface_withMissingInterface() {
+        val iface = createInterface()
+        removeInterface(iface)
+        // Interface does not exist, enable/disableInterface() should both return an error.
+        enableInterface(iface).expectError()
+        disableInterface(iface).expectError()
     }
 
     @Test
     fun testUpdateConfiguration_forBothIpConfigAndCapabilities() {
         val iface = createInterface()
-        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
-        val network = cb.expectAvailable()
-        cb.assertNeverLost()
+        val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
+        cb.expect<Available>()
 
-        val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
-        val nc = NetworkCapabilities
-                .Builder(ETH_REQUEST.networkCapabilities)
-                .addCapability(testCapability)
-                .build()
-        updateConfiguration(iface, STATIC_IP_CONFIGURATION, nc).expectResult(iface.name)
-
-        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
-        // will be expected first before available, as part of the restart.
-        cb.expectLost(network)
-        cb.expectAvailable()
-        cb.expectCapabilitiesWithCapability(testCapability)
-        cb.expectLinkPropertiesWithLinkAddress(
-                STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+        updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+        cb.eventuallyExpectCapabilities(TEST_CAPS)
+        cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
     }
 
     @Test
     fun testUpdateConfiguration_forOnlyIpConfig() {
-        val iface: EthernetTestInterface = createInterface()
-        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
-        val network = cb.expectAvailable()
-        cb.assertNeverLost()
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
+        cb.expect<Available>()
 
         updateConfiguration(iface, STATIC_IP_CONFIGURATION).expectResult(iface.name)
-
-        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
-        // will be expected first before available, as part of the restart.
-        cb.expectLost(network)
-        cb.expectAvailable()
-        cb.expectCallback<CapabilitiesChanged>()
-        cb.expectLinkPropertiesWithLinkAddress(
-                STATIC_IP_CONFIGURATION.staticIpConfiguration.ipAddress!!)
+        cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
     }
 
-    // TODO(b/240323229): This test is currently flaky due to a race between IpClient restarting and
-    // NetworkAgent tearing down the routes. This problem is exacerbated by disabling RS delay.
-    @Ignore
     @Test
     fun testUpdateConfiguration_forOnlyCapabilities() {
-        val iface: EthernetTestInterface = createInterface()
-        val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface.name))
-        val network = cb.expectAvailable()
-        cb.assertNeverLost()
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
+        cb.expect<Available>()
 
-        val testCapability = NET_CAPABILITY_TEMPORARILY_NOT_METERED
-        val nc = NetworkCapabilities
-                .Builder(ETH_REQUEST.networkCapabilities)
-                .addCapability(testCapability)
-                .build()
+        updateConfiguration(iface, capabilities = TEST_CAPS).expectResult(iface.name)
+        cb.eventuallyExpectCapabilities(TEST_CAPS)
+    }
+
+    @Test
+    fun testUpdateConfiguration_forAllowedUids() {
+        // Configure a restricted network.
+        val iface = createInterface()
+        val request = NetworkRequest.Builder(ETH_REQUEST.copyWithEthernetSpecifier(iface.name))
+                .removeCapability(NET_CAPABILITY_NOT_RESTRICTED).build()
+        updateConfiguration(iface, capabilities = request.networkCapabilities)
+                .expectResult(iface.name)
+
+        // Request the restricted network as the shell with CONNECTIVITY_USE_RESTRICTED_NETWORKS.
+        val cb = runAsShell(CONNECTIVITY_USE_RESTRICTED_NETWORKS) { requestNetwork(request) }
+        val network = cb.expect<Available>().network
+        cb.assertNeverLost(network)
+
+        // The network is restricted therefore binding to it when available will fail.
+        Socket().use { socket ->
+            assertThrows(IOException::class.java, { network.bindSocket(socket) })
+        }
+
+        // Add the test process UID to the allowed UIDs for the network and ultimately bind again.
+        val allowedUids = setOf(Process.myUid())
+        val nc = NetworkCapabilities.Builder(request.networkCapabilities)
+                .setAllowedUids(allowedUids).build()
         updateConfiguration(iface, capabilities = nc).expectResult(iface.name)
 
-        // UpdateConfiguration() currently does a restarts on the ethernet interface therefore lost
+        // UpdateConfiguration() currently does a restart on the ethernet interface therefore lost
         // will be expected first before available, as part of the restart.
-        cb.expectLost(network)
-        cb.expectAvailable()
-        cb.expectCapabilitiesWithCapability(testCapability)
+        cb.expect<Lost>(network)
+        val updatedNetwork = cb.expect<Available>().network
+        // With the test process UID allowed, binding to a restricted network should be successful.
+        Socket().use { socket -> updatedNetwork.bindSocket(socket) }
+
+        // Reset capabilities to not-restricted, otherwise tearDown won't see the interface callback
+        // as ifaceListener does not have the restricted permission.
+        // TODO: Find a better way to do this when there are more tests around restricted
+        // interfaces.
+        updateConfiguration(iface, capabilities = TEST_CAPS).expectResult(iface.name)
+    }
+
+    // TODO: consider only having this test in MTS as it makes use of the fact that
+    // setEthernetEnabled(false) untracks all interfaces. This behavior is an implementation detail
+    // and may change in the future.
+    @Test
+    fun testUpdateConfiguration_onUntrackedInterface() {
+        assumeFalse(isAdbOverEthernet())
+        val iface = createInterface()
+        setEthernetEnabled(false)
+
+        // Updating the IpConfiguration and NetworkCapabilities on absent interfaces is a supported
+        // use case.
+        updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+
+        setEthernetEnabled(true)
+        val cb = requestNetwork(ETH_REQUEST)
+        cb.expect<Available>()
+        cb.eventuallyExpectCapabilities(TEST_CAPS)
+        cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
+    }
+
+    @Test
+    fun testUpdateConfiguration_withLinkDown() {
+        assumeChangingCarrierSupported()
+        // createInterface without carrier is racy, so create it and then remove carrier.
+        val iface = createInterface()
+        val cb = requestNetwork(ETH_REQUEST)
+        cb.expect<Available>()
+
+        iface.setCarrierEnabled(false)
+        cb.eventuallyExpectLost()
+
+        updateConfiguration(iface, STATIC_IP_CONFIGURATION, TEST_CAPS).expectResult(iface.name)
+        cb.assertNoCallback()
+
+        iface.setCarrierEnabled(true)
+        cb.eventuallyExpectCapabilities(TEST_CAPS)
+        cb.eventuallyExpectLpForStaticConfig(STATIC_IP_CONFIGURATION.staticIpConfiguration)
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 2b1d173..ac50740 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -24,7 +24,6 @@
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
-import static com.android.testutils.TestableNetworkCallbackKt.anyNetwork;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -520,8 +519,7 @@
                 HexDump.hexStringToByteArray(authResp));
 
         // Verify the VPN network came up
-        final Network vpnNetwork = cb.expectCallback(CallbackEntry.AVAILABLE, anyNetwork())
-                .getNetwork();
+        final Network vpnNetwork = cb.expect(CallbackEntry.AVAILABLE).getNetwork();
 
         if (testSessionKey) {
             final VpnProfileStateShim profileState = mVmShim.getProvisionedVpnProfileState();
@@ -536,8 +534,8 @@
                 && caps.hasCapability(NET_CAPABILITY_INTERNET)
                 && !caps.hasCapability(NET_CAPABILITY_VALIDATED)
                 && Process.myUid() == caps.getOwnerUid());
-        cb.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, vpnNetwork);
-        cb.expectCallback(CallbackEntry.BLOCKED_STATUS, vpnNetwork);
+        cb.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, vpnNetwork);
+        cb.expect(CallbackEntry.BLOCKED_STATUS, vpnNetwork);
 
         // A VPN that requires validation is initially not validated, while one that doesn't
         // immediately validate automatically. Because this VPN can't actually access Internet,
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index d4f3d57..6df71c8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -73,6 +73,8 @@
 import android.os.Message
 import android.os.SystemClock
 import android.platform.test.annotations.AppModeFull
+import android.system.OsConstants.IPPROTO_TCP
+import android.system.OsConstants.IPPROTO_UDP
 import android.telephony.TelephonyManager
 import android.telephony.data.EpsBearerQosSessionAttributes
 import android.util.DebugUtils.valueToString
@@ -117,6 +119,7 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.timeout
 import org.mockito.Mockito.verify
+import java.io.Closeable
 import java.io.IOException
 import java.net.DatagramSocket
 import java.net.InetAddress
@@ -174,7 +177,7 @@
     private val mFakeConnectivityService = FakeConnectivityService()
     private val agentsToCleanUp = mutableListOf<NetworkAgent>()
     private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
-    private var qosTestSocket: Socket? = null
+    private var qosTestSocket: Closeable? = null // either Socket or DatagramSocket
 
     @Before
     fun setUp() {
@@ -332,6 +335,28 @@
         mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
     }
 
+    fun assertLinkPropertiesEventually(
+        n: Network,
+        description: String,
+        condition: (LinkProperties?) -> Boolean
+    ): LinkProperties? {
+        val deadline = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS
+        do {
+            val lp = mCM.getLinkProperties(n)
+            if (condition(lp)) return lp
+            SystemClock.sleep(10 /* ms */)
+        } while (SystemClock.elapsedRealtime() < deadline)
+        fail("Network $n LinkProperties did not $description after $DEFAULT_TIMEOUT_MS ms")
+    }
+
+    fun assertLinkPropertiesEventuallyNotNull(n: Network) {
+        assertLinkPropertiesEventually(n, "become non-null") { it != null }
+    }
+
+    fun assertLinkPropertiesEventuallyNull(n: Network) {
+        assertLinkPropertiesEventually(n, "become null") { it == null }
+    }
+
     @Test
     fun testSetSubtypeNameAndExtraInfoByAgentConfig() {
         val subtypeLTE = TelephonyManager.NETWORK_TYPE_LTE
@@ -378,7 +403,7 @@
     fun testConnectAndUnregister() {
         val (agent, callback) = createConnectedNetworkAgent()
         unregister(agent)
-        callback.expectCallback<Lost>(agent.network!!)
+        callback.expect<Lost>(agent.network!!)
         assertFailsWith<IllegalStateException>("Must not be able to register an agent twice") {
             agent.register()
         }
@@ -429,7 +454,7 @@
             agent.sendNetworkCapabilities(nc)
             callbacks[0].expectCapabilitiesThat(agent.network!!) { it.signalStrength == 55 }
             callbacks[1].expectCapabilitiesThat(agent.network!!) { it.signalStrength == 55 }
-            callbacks[2].expectCallback<Lost>(agent.network!!)
+            callbacks[2].expect<Lost>(agent.network!!)
         }
         callbacks.forEach {
             mCM.unregisterNetworkCallback(it)
@@ -507,12 +532,12 @@
         agent.markConnected()
 
         // Make sure the UIDs have been ignored.
-        callback.expectCallback<Available>(agent.network!!)
+        callback.expect<Available>(agent.network!!)
         callback.expectCapabilitiesThat(agent.network!!) {
             it.allowedUids.isEmpty() && !it.hasCapability(NET_CAPABILITY_VALIDATED)
         }
-        callback.expectCallback<LinkPropertiesChanged>(agent.network!!)
-        callback.expectCallback<BlockedStatus>(agent.network!!)
+        callback.expect<LinkPropertiesChanged>(agent.network!!)
+        callback.expect<BlockedStatus>(agent.network!!)
         callback.expectCapabilitiesThat(agent.network!!) {
             it.allowedUids.isEmpty() && it.hasCapability(NET_CAPABILITY_VALIDATED)
         }
@@ -552,7 +577,7 @@
                 .setLegacyInt(WORSE_NETWORK_SCORE)
                 .setExiting(true)
                 .build())
-        callback.expectCallback<Available>(agent2.network!!)
+        callback.expect<Available>(agent2.network!!)
 
         // tearDown() will unregister the requests and agents
     }
@@ -636,7 +661,7 @@
         }
 
         unregister(agent)
-        callback.expectCallback<Lost>(agent.network!!)
+        callback.expect<Lost>(agent.network!!)
     }
 
     private fun unregister(agent: TestableNetworkAgent) {
@@ -809,14 +834,14 @@
         agentStronger.register()
         agentStronger.markConnected()
         commonCallback.expectAvailableCallbacks(agentStronger.network!!)
-        callbackWeaker.expectCallback<Losing>(agentWeaker.network!!)
+        callbackWeaker.expect<Losing>(agentWeaker.network!!)
         val expectedRemainingLingerDuration = lingerStart +
                 NetworkAgent.MIN_LINGER_TIMER_MS.toLong() - SystemClock.elapsedRealtime()
         // If the available callback is too late. The remaining duration will be reduced.
         assertTrue(expectedRemainingLingerDuration > 0,
                 "expected remaining linger duration is $expectedRemainingLingerDuration")
         callbackWeaker.assertNoCallback(expectedRemainingLingerDuration)
-        callbackWeaker.expectCallback<Lost>(agentWeaker.network!!)
+        callbackWeaker.expect<Lost>(agentWeaker.network!!)
     }
 
     @Test
@@ -883,7 +908,7 @@
         bestMatchingCb.assertNoCallback()
         agent1.unregister()
         agentsToCleanUp.remove(agent1)
-        bestMatchingCb.expectCallback<Lost>(agent1.network!!)
+        bestMatchingCb.expect<Lost>(agent1.network!!)
 
         // tearDown() will unregister the requests and agents
     }
@@ -930,34 +955,49 @@
         }
     }
 
-    private fun setupForQosCallbackTesting(): Pair<TestableNetworkAgent, Socket> {
-        val request = NetworkRequest.Builder()
-                .clearCapabilities()
-                .addTransportType(TRANSPORT_TEST)
-                .build()
+    private fun <T : Closeable> setupForQosCallbackTest(creator: (TestableNetworkAgent) -> T) =
+            createConnectedNetworkAgent().first.let { Pair(it, creator(it)) }
 
-        val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
-        requestNetwork(request, callback)
-        val (agent, _) = createConnectedNetworkAgent()
+    private fun setupForQosSocket() = setupForQosCallbackTest {
+        agent: TestableNetworkAgent -> Socket()
+            .also { assertNotNull(agent.network?.bindSocket(it))
+                it.bind(InetSocketAddress(InetAddress.getLoopbackAddress(), 0)) }
+    }
 
-        qosTestSocket = assertNotNull(agent.network?.socketFactory?.createSocket()).also {
-            it.bind(InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
-        }
-        return Pair(agent, qosTestSocket!!)
+    private fun setupForQosDatagram() = setupForQosCallbackTest {
+        agent: TestableNetworkAgent -> DatagramSocket(
+            InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
+            .also { assertNotNull(agent.network?.bindSocket(it)) }
     }
 
     @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
     @Test
-    fun testQosCallbackRegisterWithUnregister() {
-        val (agent, socket) = setupForQosCallbackTesting()
+    fun testQosCallbackRegisterAndUnregister() {
+        validateQosCallbackRegisterAndUnregister(IPPROTO_TCP)
+    }
 
+    @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
+    @Test
+    fun testQosCallbackRegisterAndUnregisterWithDatagramSocket() {
+        validateQosCallbackRegisterAndUnregister(IPPROTO_UDP)
+    }
+
+    private fun validateQosCallbackRegisterAndUnregister(proto: Int) {
+        val (agent, qosTestSocket) = when (proto) {
+            IPPROTO_TCP -> setupForQosSocket()
+            IPPROTO_UDP -> setupForQosDatagram()
+            else -> fail("unsupported protocol")
+        }
         val qosCallback = TestableQosCallback()
         var callbackId = -1
         Executors.newSingleThreadExecutor().let { executor ->
             try {
-                val info = QosSocketInfo(agent.network!!, socket)
+                val info = QosSocketInfo(agent, qosTestSocket)
                 mCM.registerQosCallback(info, executor, qosCallback)
-                callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
+                agent.expectCallback<OnRegisterQosCallback>().let {
+                    callbackId = it.callbackId
+                    assertTrue(it.filter.matchesProtocol(proto))
+                }
 
                 assertFailsWith<QosCallbackRegistrationException>(
                         "The same callback cannot be " +
@@ -965,7 +1005,7 @@
                     mCM.registerQosCallback(info, executor, qosCallback)
                 }
             } finally {
-                socket.close()
+                qosTestSocket.close()
                 mCM.unregisterQosCallback(qosCallback)
                 agent.expectCallback<OnUnregisterQosCallback> { it.callbackId == callbackId }
                 executor.shutdown()
@@ -976,11 +1016,31 @@
     @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
     @Test
     fun testQosCallbackOnQosSession() {
-        val (agent, socket) = setupForQosCallbackTesting()
+        validateQosCallbackOnQosSession(IPPROTO_TCP)
+    }
+
+    @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
+    @Test
+    fun testQosCallbackOnQosSessionWithDatagramSocket() {
+        validateQosCallbackOnQosSession(IPPROTO_UDP)
+    }
+
+    fun QosSocketInfo(agent: NetworkAgent, socket: Closeable) = when (socket) {
+        is Socket -> QosSocketInfo(agent.network, socket)
+        is DatagramSocket -> QosSocketInfo(agent.network, socket)
+        else -> fail("unexpected socket type")
+    }
+
+    private fun validateQosCallbackOnQosSession(proto: Int) {
+        val (agent, qosTestSocket) = when (proto) {
+            IPPROTO_TCP -> setupForQosSocket()
+            IPPROTO_UDP -> setupForQosDatagram()
+            else -> fail("unsupported protocol")
+        }
         val qosCallback = TestableQosCallback()
         Executors.newSingleThreadExecutor().let { executor ->
             try {
-                val info = QosSocketInfo(agent.network!!, socket)
+                val info = QosSocketInfo(agent, qosTestSocket)
                 assertEquals(agent.network, info.getNetwork())
                 mCM.registerQosCallback(info, executor, qosCallback)
                 val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
@@ -1009,8 +1069,7 @@
                 agent.sendQosSessionLost(callbackId, sessId, QosSession.TYPE_EPS_BEARER)
                 qosCallback.assertNoCallback()
             } finally {
-                socket.close()
-
+                qosTestSocket.close()
                 // safety precaution
                 mCM.unregisterQosCallback(qosCallback)
 
@@ -1022,11 +1081,11 @@
     @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
     @Test
     fun testQosCallbackOnError() {
-        val (agent, socket) = setupForQosCallbackTesting()
+        val (agent, qosTestSocket) = setupForQosSocket()
         val qosCallback = TestableQosCallback()
         Executors.newSingleThreadExecutor().let { executor ->
             try {
-                val info = QosSocketInfo(agent.network!!, socket)
+                val info = QosSocketInfo(agent.network!!, qosTestSocket)
                 mCM.registerQosCallback(info, executor, qosCallback)
                 val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
 
@@ -1048,7 +1107,7 @@
                 agent.sendQosSessionLost(callbackId, sessId, QosSession.TYPE_EPS_BEARER)
                 qosCallback.assertNoCallback()
             } finally {
-                socket.close()
+                qosTestSocket.close()
 
                 // Make sure that the callback is fully unregistered
                 mCM.unregisterQosCallback(qosCallback)
@@ -1061,12 +1120,12 @@
     @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
     @Test
     fun testQosCallbackIdsAreMappedCorrectly() {
-        val (agent, socket) = setupForQosCallbackTesting()
+        val (agent, qosTestSocket) = setupForQosSocket()
         val qosCallback1 = TestableQosCallback()
         val qosCallback2 = TestableQosCallback()
         Executors.newSingleThreadExecutor().let { executor ->
             try {
-                val info = QosSocketInfo(agent.network!!, socket)
+                val info = QosSocketInfo(agent.network!!, qosTestSocket)
                 mCM.registerQosCallback(info, executor, qosCallback1)
                 val callbackId1 = agent.expectCallback<OnRegisterQosCallback>().callbackId
 
@@ -1088,7 +1147,7 @@
                 qosCallback1.assertNoCallback()
                 qosCallback2.expectCallback<OnQosSessionAvailable> { sessId2 == it.sess.sessionId }
             } finally {
-                socket.close()
+                qosTestSocket.close()
 
                 // Make sure that the callback is fully unregistered
                 mCM.unregisterQosCallback(qosCallback1)
@@ -1102,13 +1161,13 @@
     @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
     @Test
     fun testQosCallbackWhenNetworkReleased() {
-        val (agent, socket) = setupForQosCallbackTesting()
+        val (agent, qosTestSocket) = setupForQosSocket()
         Executors.newSingleThreadExecutor().let { executor ->
             try {
                 val qosCallback1 = TestableQosCallback()
                 val qosCallback2 = TestableQosCallback()
                 try {
-                    val info = QosSocketInfo(agent.network!!, socket)
+                    val info = QosSocketInfo(agent.network!!, qosTestSocket)
                     mCM.registerQosCallback(info, executor, qosCallback1)
                     mCM.registerQosCallback(info, executor, qosCallback2)
                     agent.unregister()
@@ -1121,12 +1180,12 @@
                         it.ex.cause is NetworkReleasedException
                     }
                 } finally {
-                    socket.close()
+                    qosTestSocket.close()
                     mCM.unregisterQosCallback(qosCallback1)
                     mCM.unregisterQosCallback(qosCallback2)
                 }
             } finally {
-                socket.close()
+                qosTestSocket.close()
                 executor.shutdown()
             }
         }
@@ -1163,7 +1222,7 @@
         // testCallback will not see any events. agent2 is be torn down because it has no requests.
         val (agent2, network2) = connectNetwork()
         matchAllCallback.expectAvailableThenValidatedCallbacks(network2)
-        matchAllCallback.expectCallback<Lost>(network2)
+        matchAllCallback.expect<Lost>(network2)
         agent2.expectCallback<OnNetworkUnwanted>()
         agent2.expectCallback<OnNetworkDestroyed>()
         assertNull(mCM.getLinkProperties(network2))
@@ -1191,7 +1250,7 @@
 
         // As soon as the replacement arrives, network1 is disconnected.
         // Check that this happens before the replacement timeout (5 seconds) fires.
-        matchAllCallback.expectCallback<Lost>(network1, 2_000 /* timeoutMs */)
+        matchAllCallback.expect<Lost>(network1, 2_000 /* timeoutMs */)
         agent1.expectCallback<OnNetworkUnwanted>()
 
         // Test lingering:
@@ -1204,19 +1263,19 @@
         val network4 = agent4.network!!
         matchAllCallback.expectAvailableThenValidatedCallbacks(network4)
         agent4.sendNetworkScore(NetworkScore.Builder().setTransportPrimary(true).build())
-        matchAllCallback.expectCallback<Losing>(network3)
+        matchAllCallback.expect<Losing>(network3)
         testCallback.expectAvailableCallbacks(network4, validated = true)
         mCM.unregisterNetworkCallback(agent4callback)
         agent3.unregisterAfterReplacement(5_000)
         agent3.expectCallback<OnNetworkUnwanted>()
-        matchAllCallback.expectCallback<Lost>(network3, 1000L)
+        matchAllCallback.expect<Lost>(network3, 1000L)
         agent3.expectCallback<OnNetworkDestroyed>()
 
         // Now mark network4 awaiting replacement with a low timeout, and check that if no
         // replacement arrives, it is torn down.
         agent4.unregisterAfterReplacement(100 /* timeoutMillis */)
-        matchAllCallback.expectCallback<Lost>(network4, 1000L /* timeoutMs */)
-        testCallback.expectCallback<Lost>(network4, 1000L /* timeoutMs */)
+        matchAllCallback.expect<Lost>(network4, 1000L /* timeoutMs */)
+        testCallback.expect<Lost>(network4, 1000L /* timeoutMs */)
         agent4.expectCallback<OnNetworkDestroyed>()
         agent4.expectCallback<OnNetworkUnwanted>()
 
@@ -1227,13 +1286,45 @@
         testCallback.expectAvailableThenValidatedCallbacks(network5)
         agent5.unregisterAfterReplacement(5_000 /* timeoutMillis */)
         agent5.unregister()
-        matchAllCallback.expectCallback<Lost>(network5, 1000L /* timeoutMs */)
-        testCallback.expectCallback<Lost>(network5, 1000L /* timeoutMs */)
+        matchAllCallback.expect<Lost>(network5, 1000L /* timeoutMs */)
+        testCallback.expect<Lost>(network5, 1000L /* timeoutMs */)
         agent5.expectCallback<OnNetworkDestroyed>()
         agent5.expectCallback<OnNetworkUnwanted>()
 
+        // If unregisterAfterReplacement is called before markConnected, the network disconnects.
+        val specifier6 = UUID.randomUUID().toString()
+        val callback = TestableNetworkCallback()
+        requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
+        val agent6 = createNetworkAgent(specifier = specifier6)
+        val network6 = agent6.register()
+        // No callbacks are sent, so check the LinkProperties to see if the network has connected.
+        assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+
+        // unregisterAfterReplacement tears down the network immediately.
+        // Approximately check that this is the case by picking an unregister timeout that's longer
+        // than the timeout of the expectCallback<OnNetworkUnwanted> below.
+        // TODO: consider adding configurable timeouts to TestableNetworkAgent expectations.
+        val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
+        agent6.unregisterAfterReplacement(timeoutMs)
+        agent6.expectCallback<OnNetworkUnwanted>()
+        if (!SdkLevel.isAtLeastT()) {
+            // Before T, onNetworkDestroyed is called even if the network was never created.
+            agent6.expectCallback<OnNetworkDestroyed>()
+        }
+        // Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
+        // network has not yet been removed from the CS data structures.
+        assertLinkPropertiesEventuallyNull(agent6.network!!)
+        assertFalse(mCM.getAllNetworks().contains(agent6.network!!))
+
+        // After unregisterAfterReplacement is called, the network is no longer usable and
+        // markConnected has no effect.
+        agent6.markConnected()
+        agent6.assertNoCallback()
+        assertNull(mCM.getLinkProperties(agent6.network!!))
+        matchAllCallback.assertNoCallback(200 /* timeoutMs */)
+
         // If wifi is replaced within the timeout, the device does not switch to cellular.
-        val (cellAgent, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
+        val (_, cellNetwork) = connectNetwork(TRANSPORT_CELLULAR)
         testCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
         matchAllCallback.expectAvailableThenValidatedCallbacks(cellNetwork)
 
@@ -1243,7 +1334,7 @@
             it.hasCapability(NET_CAPABILITY_VALIDATED)
         }
         matchAllCallback.expectAvailableCallbacks(wifiNetwork, validated = false)
-        matchAllCallback.expectCallback<Losing>(cellNetwork)
+        matchAllCallback.expect<Losing>(cellNetwork)
         matchAllCallback.expectCapabilitiesThat(wifiNetwork) {
             it.hasCapability(NET_CAPABILITY_VALIDATED)
         }
@@ -1272,7 +1363,7 @@
         val (newWifiAgent, newWifiNetwork) = connectNetwork(TRANSPORT_WIFI)
         testCallback.expectAvailableCallbacks(newWifiNetwork, validated = true)
         matchAllCallback.expectAvailableThenValidatedCallbacks(newWifiNetwork)
-        matchAllCallback.expectCallback<Lost>(wifiNetwork)
+        matchAllCallback.expect<Lost>(wifiNetwork)
         wifiAgent.expectCallback<OnNetworkUnwanted>()
     }
 
@@ -1291,7 +1382,7 @@
         // lost callback should be sent still.
         agent.markConnected()
         agent.unregister()
-        callback.expectCallback<Available>(agent.network!!)
+        callback.expect<Available>(agent.network!!)
         callback.eventuallyExpect<Lost> { it.network == agent.network }
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index 8f17199..eb41d71 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -34,6 +34,7 @@
 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.RecorderCallback.CallbackEntry
 import com.android.testutils.TestableNetworkCallback
 import com.android.testutils.TestableNetworkCallback.HasNetwork
 import org.junit.After
@@ -76,7 +77,18 @@
 
     @After
     fun tearDown() {
-        agentsToCleanUp.forEach { it.unregister() }
+        val agentCleanUpCb = TestableNetworkCallback(TIMEOUT_MS).also { cb ->
+            mCm.registerNetworkCallback(
+                NetworkRequest.Builder().clearCapabilities()
+                    .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler
+            )
+        }
+        agentsToCleanUp.forEach {
+            it.unregister()
+            agentCleanUpCb.eventuallyExpect<CallbackEntry.Lost> { cb -> cb.network == it.network }
+        }
+        mCm.unregisterNetworkCallback(agentCleanUpCb)
+
         mHandlerThread.quitSafely()
         callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
     }
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
index 375bfb8..a0b40aa 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -20,6 +20,7 @@
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
 import com.android.net.module.util.NetworkStackConstants
+import com.android.testutils.DeviceConfigRule
 import com.android.testutils.runAsShell
 
 /**
@@ -27,7 +28,6 @@
  */
 internal object NetworkValidationTestUtil {
     val TAG = NetworkValidationTestUtil::class.simpleName
-    const val TIMEOUT_MS = 20_000L
 
     /**
      * Clear the test network validation URLs.
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index a02be85..2b5c305 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -65,6 +65,7 @@
 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
 import com.android.testutils.runAsShell
 import com.android.testutils.tryTest
+import com.android.testutils.waitForIdle
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertFalse
@@ -87,9 +88,13 @@
 import kotlin.test.fail
 
 private const val TAG = "NsdManagerTest"
-private const val SERVICE_TYPE = "_nmt._tcp"
 private const val TIMEOUT_MS = 2000L
 private const val NO_CALLBACK_TIMEOUT_MS = 200L
+// Registration may take a long time if there are devices with the same hostname on the network,
+// as the device needs to try another name and probe again. This is especially true since when using
+// mdnsresponder the usual hostname is "Android", and on conflict "Android-2", "Android-3", ... are
+// tried sequentially
+private const val REGISTRATION_TIMEOUT_MS = 10_000L
 private const val DBG = false
 
 private val nsdShim = NsdShimImpl.newInstance()
@@ -106,6 +111,7 @@
 
     private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
     private val serviceName = "NsdTest%09d".format(Random().nextInt(1_000_000_000))
+    private val serviceType = "_nmt%09d._tcp".format(Random().nextInt(1_000_000_000))
     private val handlerThread = HandlerThread(NsdManagerTest::class.java.simpleName)
 
     private lateinit var testNetwork1: TestTapNetwork
@@ -121,6 +127,7 @@
             cm.unregisterNetworkCallback(requestCb)
             agent.unregister()
             iface.fileDescriptor.close()
+            agent.waitForIdle(TIMEOUT_MS)
         }
     }
 
@@ -141,13 +148,14 @@
         }
 
         inline fun <reified V : NsdEvent> expectCallbackEventually(
+            timeoutMs: Long = TIMEOUT_MS,
             crossinline predicate: (V) -> Boolean = { true }
-        ): V = nextEvents.poll(TIMEOUT_MS) { e -> e is V && predicate(e) } as V?
-                ?: fail("Callback for ${V::class.java.simpleName} not seen after $TIMEOUT_MS ms")
+        ): V = nextEvents.poll(timeoutMs) { e -> e is V && predicate(e) } as V?
+                ?: fail("Callback for ${V::class.java.simpleName} not seen after $timeoutMs ms")
 
-        inline fun <reified V : NsdEvent> expectCallback(): V {
-            val nextEvent = nextEvents.poll(TIMEOUT_MS)
-            assertNotNull(nextEvent, "No callback received after $TIMEOUT_MS ms")
+        inline fun <reified V : NsdEvent> expectCallback(timeoutMs: Long = TIMEOUT_MS): V {
+            val nextEvent = nextEvents.poll(timeoutMs)
+            assertNotNull(nextEvent, "No callback received after $timeoutMs ms")
             assertTrue(nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
                     nextEvent.javaClass.simpleName)
             return nextEvent
@@ -291,7 +299,7 @@
         val agent = registerTestNetworkAgent(iface.interfaceName)
         val network = agent.network ?: fail("Registered agent should have a network")
         // The network has no INTERNET capability, so will be marked validated immediately
-        cb.expectAvailableThenValidatedCallbacks(network)
+        cb.expectAvailableThenValidatedCallbacks(network, TIMEOUT_MS)
         return TestTapNetwork(iface, cb, agent, network)
     }
 
@@ -319,13 +327,14 @@
                 testNetwork2.close(cm)
             }
         }
+        handlerThread.waitForIdle(TIMEOUT_MS)
         handlerThread.quitSafely()
     }
 
     @Test
     fun testNsdManager() {
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = serviceName
         // Test binary data with various bytes
         val testByteArray = byteArrayOf(-128, 127, 2, 1, 0, 1, 2)
@@ -370,11 +379,12 @@
         val registrationRecord = NsdRegistrationRecord()
         // Test registering without an Executor
         nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
-        val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>().serviceInfo
+        val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>(
+                REGISTRATION_TIMEOUT_MS).serviceInfo
 
         val discoveryRecord = NsdDiscoveryRecord()
         // Test discovering without an Executor
-        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
+        nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
 
         // Expect discovery started
         discoveryRecord.expectCallback<DiscoveryStarted>()
@@ -414,12 +424,13 @@
 
         // Register service again to see if NsdManager can discover it
         val si2 = NsdServiceInfo()
-        si2.serviceType = SERVICE_TYPE
+        si2.serviceType = serviceType
         si2.serviceName = serviceName
         si2.port = localPort
         val registrationRecord2 = NsdRegistrationRecord()
         nsdManager.registerService(si2, NsdManager.PROTOCOL_DNS_SD, registrationRecord2)
-        val registeredInfo2 = registrationRecord2.expectCallback<ServiceRegistered>().serviceInfo
+        val registeredInfo2 = registrationRecord2.expectCallback<ServiceRegistered>(
+                REGISTRATION_TIMEOUT_MS).serviceInfo
 
         // Expect a service record to be discovered (and filter the ones
         // that are unrelated to this test)
@@ -447,7 +458,7 @@
         assumeTrue(TestUtils.shouldTestTApis())
 
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = this.serviceName
         si.port = 12345 // Test won't try to connect so port does not matter
 
@@ -456,7 +467,7 @@
 
         tryTest {
             val discoveryRecord = NsdDiscoveryRecord()
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                     testNetwork1.network, Executor { it.run() }, discoveryRecord)
 
             val foundInfo = discoveryRecord.waitForServiceDiscovered(
@@ -481,7 +492,7 @@
         assumeTrue(TestUtils.shouldTestTApis())
 
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = this.serviceName
         si.port = 12345 // Test won't try to connect so port does not matter
 
@@ -494,7 +505,7 @@
 
         tryTest {
             val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                     NetworkRequest.Builder()
                             .removeCapability(NET_CAPABILITY_TRUSTED)
                             .addTransportType(TRANSPORT_TEST)
@@ -503,7 +514,7 @@
                     executor, discoveryRecord)
 
             val discoveryStarted = discoveryRecord.expectCallback<DiscoveryStarted>()
-            assertEquals(SERVICE_TYPE, discoveryStarted.serviceType)
+            assertEquals(serviceType, discoveryStarted.serviceType)
 
             val serviceDiscovered = discoveryRecord.expectCallback<ServiceFound>()
             assertEquals(registeredInfo1.serviceName, serviceDiscovered.serviceInfo.serviceName)
@@ -549,7 +560,7 @@
         assumeTrue(TestUtils.shouldTestTApis())
 
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = this.serviceName
         si.port = 12345 // Test won't try to connect so port does not matter
 
@@ -560,7 +571,7 @@
         val specifier = TestNetworkSpecifier(testNetwork1.iface.interfaceName)
 
         tryTest {
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                     NetworkRequest.Builder()
                             .removeCapability(NET_CAPABILITY_TRUSTED)
                             .addTransportType(TRANSPORT_TEST)
@@ -582,7 +593,7 @@
         assumeTrue(TestUtils.shouldTestTApis())
 
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = this.serviceName
         si.port = 12345 // Test won't try to connect so port does not matter
 
@@ -592,7 +603,7 @@
             val resolveRecord = NsdResolveRecord()
 
             val discoveryRecord = NsdDiscoveryRecord()
-            nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
+            nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
 
             val foundInfo1 = discoveryRecord.waitForServiceDiscovered(
                     serviceName, testNetwork1.network)
@@ -607,7 +618,7 @@
             val cb = resolveRecord.expectCallback<ServiceResolved>()
             cb.serviceInfo.let {
                 // Resolved service type has leading dot
-                assertEquals(".$SERVICE_TYPE", it.serviceType)
+                assertEquals(".$serviceType", it.serviceType)
                 assertEquals(registeredInfo.serviceName, it.serviceName)
                 assertEquals(si.port, it.port)
                 assertEquals(testNetwork1.network, nsdShim.getNetwork(it))
@@ -626,7 +637,7 @@
         assumeTrue(TestUtils.shouldTestTApis())
 
         val si = NsdServiceInfo()
-        si.serviceType = SERVICE_TYPE
+        si.serviceType = serviceType
         si.serviceName = this.serviceName
         si.network = testNetwork1.network
         si.port = 12345 // Test won't try to connect so port does not matter
@@ -640,7 +651,7 @@
 
         tryTest {
             // Discover service on testNetwork1.
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                 testNetwork1.network, Executor { it.run() }, discoveryRecord)
             // Expect that service is found on testNetwork1
             val foundInfo = discoveryRecord.waitForServiceDiscovered(
@@ -648,14 +659,14 @@
             assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
 
             // Discover service on testNetwork2.
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                 testNetwork2.network, Executor { it.run() }, discoveryRecord2)
             // Expect that discovery is started then no other callbacks.
             discoveryRecord2.expectCallback<DiscoveryStarted>()
             discoveryRecord2.assertNoCallback()
 
             // Discover service on all networks (not specify any network).
-            nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+            nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
                 null as Network? /* network */, Executor { it.run() }, discoveryRecord3)
             // Expect that service is found on testNetwork1
             val foundInfo3 = discoveryRecord3.waitForServiceDiscovered(
@@ -673,7 +684,7 @@
     fun testNsdManager_RegisterServiceNameWithNonStandardCharacters() {
         val serviceNames = "^Nsd.Test|Non-#AsCiI\\Characters&\\ufffe テスト 測試"
         val si = NsdServiceInfo().apply {
-            serviceType = SERVICE_TYPE
+            serviceType = this@NsdManagerTest.serviceType
             serviceName = serviceNames
             port = 12345 // Test won't try to connect so port does not matter
         }
@@ -681,13 +692,13 @@
         // Register the service name which contains non-standard characters.
         val registrationRecord = NsdRegistrationRecord()
         nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
-        registrationRecord.expectCallback<ServiceRegistered>()
+        registrationRecord.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
 
         tryTest {
             // Discover that service name.
             val discoveryRecord = NsdDiscoveryRecord()
             nsdManager.discoverServices(
-                SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord
+                serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord
             )
             val foundInfo = discoveryRecord.waitForServiceDiscovered(serviceNames)
 
@@ -739,7 +750,7 @@
         nsdShim.registerService(nsdManager, si, NsdManager.PROTOCOL_DNS_SD, executor, record)
         // We may not always get the name that we tried to register;
         // This events tells us the name that was registered.
-        val cb = record.expectCallback<ServiceRegistered>()
+        val cb = record.expectCallback<ServiceRegistered>(REGISTRATION_TIMEOUT_MS)
         return cb.serviceInfo
     }
 
diff --git a/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
index cd43a34..04abd72 100644
--- a/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
+++ b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
@@ -17,6 +17,7 @@
 package android.net.cts;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -24,8 +25,11 @@
 import android.net.QosCallbackException;
 import android.net.SocketLocalAddressChangedException;
 import android.net.SocketNotBoundException;
+import android.net.SocketNotConnectedException;
+import android.net.SocketRemoteAddressChangedException;
 import android.os.Build;
 
+import com.android.testutils.ConnectivityModuleTest;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
 
@@ -34,14 +38,9 @@
 
 @RunWith(DevSdkIgnoreRunner.class)
 @IgnoreUpTo(Build.VERSION_CODES.R)
+@ConnectivityModuleTest
 public class QosCallbackExceptionTest {
     private static final String ERROR_MESSAGE = "Test Error Message";
-    private static final String ERROR_MSG_SOCK_NOT_BOUND = "The socket is unbound";
-    private static final String ERROR_MSG_NET_RELEASED =
-            "The network was released and is no longer available";
-    private static final String ERROR_MSG_SOCK_ADDR_CHANGED =
-            "The local address of the socket changed";
-
 
     @Test
     public void testQosCallbackException() throws Exception {
@@ -57,33 +56,65 @@
     public void testNetworkReleasedExceptions() throws Exception {
         final Throwable netReleasedException = new NetworkReleasedException();
         final QosCallbackException exception = new QosCallbackException(netReleasedException);
-
-        assertTrue(exception.getCause() instanceof NetworkReleasedException);
-        assertEquals(netReleasedException, exception.getCause());
-        assertTrue(exception.getMessage().contains(ERROR_MSG_NET_RELEASED));
-        assertThrowableMessageContains(exception, ERROR_MSG_NET_RELEASED);
+        validateQosCallbackException(
+                exception, netReleasedException, NetworkReleasedException.class);
     }
 
     @Test
     public void testSocketNotBoundExceptions() throws Exception {
         final Throwable sockNotBoundException = new SocketNotBoundException();
         final QosCallbackException exception = new QosCallbackException(sockNotBoundException);
-
-        assertTrue(exception.getCause() instanceof SocketNotBoundException);
-        assertEquals(sockNotBoundException, exception.getCause());
-        assertTrue(exception.getMessage().contains(ERROR_MSG_SOCK_NOT_BOUND));
-        assertThrowableMessageContains(exception, ERROR_MSG_SOCK_NOT_BOUND);
+        validateQosCallbackException(
+                exception, sockNotBoundException, SocketNotBoundException.class);
     }
 
     @Test
     public void testSocketLocalAddressChangedExceptions() throws  Exception {
-        final Throwable localAddrChangedException = new SocketLocalAddressChangedException();
-        final QosCallbackException exception = new QosCallbackException(localAddrChangedException);
+        final Throwable localAddressChangedException = new SocketLocalAddressChangedException();
+        final QosCallbackException exception =
+                new QosCallbackException(localAddressChangedException);
+        validateQosCallbackException(
+                exception, localAddressChangedException, SocketLocalAddressChangedException.class);
+    }
 
-        assertTrue(exception.getCause() instanceof SocketLocalAddressChangedException);
-        assertEquals(localAddrChangedException, exception.getCause());
-        assertTrue(exception.getMessage().contains(ERROR_MSG_SOCK_ADDR_CHANGED));
-        assertThrowableMessageContains(exception, ERROR_MSG_SOCK_ADDR_CHANGED);
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S)
+    public void testSocketNotConnectedExceptions() throws Exception {
+        final Throwable sockNotConnectedException = new SocketNotConnectedException();
+        final QosCallbackException exception = new QosCallbackException(sockNotConnectedException);
+        validateQosCallbackException(
+                exception, sockNotConnectedException, SocketNotConnectedException.class);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S)
+    public void testSocketRemoteAddressChangedExceptions() throws  Exception {
+        final Throwable remoteAddressChangedException = new SocketRemoteAddressChangedException();
+        final QosCallbackException exception =
+                new QosCallbackException(remoteAddressChangedException);
+        validateQosCallbackException(
+                exception, remoteAddressChangedException,
+                SocketRemoteAddressChangedException.class);
+    }
+
+    private void validateQosCallbackException(
+            QosCallbackException e, Throwable cause, Class c) throws Exception {
+        if (c == SocketNotConnectedException.class) {
+            assertTrue(e.getCause() instanceof SocketNotConnectedException);
+        } else if (c == SocketRemoteAddressChangedException.class) {
+            assertTrue(e.getCause() instanceof SocketRemoteAddressChangedException);
+        } else if (c == SocketLocalAddressChangedException.class) {
+            assertTrue(e.getCause() instanceof SocketLocalAddressChangedException);
+        } else if (c == SocketNotBoundException.class) {
+            assertTrue(e.getCause() instanceof SocketNotBoundException);
+        } else if (c == NetworkReleasedException.class) {
+            assertTrue(e.getCause() instanceof NetworkReleasedException);
+        } else {
+            fail("unexpected error msg.");
+        }
+        assertEquals(cause, e.getCause());
+        assertFalse(e.getMessage().isEmpty());
+        assertThrowableMessageContains(e, e.getMessage());
     }
 
     private void assertThrowableMessageContains(QosCallbackException exception, String errorMsg)
diff --git a/tests/cts/net/src/android/net/cts/TestUtils.java b/tests/cts/net/src/android/net/cts/TestUtils.java
index 001aa01..6180845 100644
--- a/tests/cts/net/src/android/net/cts/TestUtils.java
+++ b/tests/cts/net/src/android/net/cts/TestUtils.java
@@ -16,8 +16,6 @@
 
 package android.net.cts;
 
-import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
-
 import android.os.Build;
 
 import com.android.modules.utils.build.SdkLevel;
@@ -37,11 +35,18 @@
     }
 
     /**
-     * Whether to test T+ APIs. This requires a) that the test be running on an S+ device, and
+     * Whether to test T+ APIs. This requires a) that the test be running on an T+ device, and
      * b) that the code be compiled against shims new enough to access these APIs.
      */
     public static boolean shouldTestTApis() {
-        // TODO: replace SC_V2 with Build.VERSION_CODES.S_V2 when it's available in mainline branch.
-        return SdkLevel.isAtLeastT() && ConstantsShim.VERSION > SC_V2;
+        return SdkLevel.isAtLeastT() && ConstantsShim.VERSION > Build.VERSION_CODES.S_V2;
+    }
+
+    /**
+     * Whether to test U+ APIs. This requires a) that the test be running on an U+ device, and
+     * b) that the code be compiled against shims new enough to access these APIs.
+     */
+    public static boolean shouldTestUApis() {
+        return SdkLevel.isAtLeastU() && ConstantsShim.VERSION > Build.VERSION_CODES.TIRAMISU;
     }
 }
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 9d1fa60..3f2ff9d 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -57,6 +57,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.net.module.util.ConnectivitySettingsUtils;
 import com.android.testutils.ConnectUtil;
@@ -268,6 +270,19 @@
     }
 
     /**
+     * Disable WiFi and wait for the connection info to be cleared.
+     */
+    public void disableWifi() throws Exception {
+        SystemUtil.runShellCommand("svc wifi disable");
+        PollingCheck.check(
+                "Wifi not disconnected! Current network is not null "
+                        + mWifiManager.getConnectionInfo().getNetworkId(),
+                TimeUnit.SECONDS.toMillis(CONNECTIVITY_CHANGE_TIMEOUT_SECS),
+                () -> ShellIdentityUtils.invokeWithShellPermissions(
+                        () -> mWifiManager.getConnectionInfo().getNetworkId()) == -1);
+    }
+
+    /**
      * Disable WiFi and wait for it to become disconnected from the network.
      *
      * @param wifiNetworkToCheck If non-null, a network that should be disconnected. This network
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index 8b904bc..f506c23 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -478,7 +478,9 @@
             waitForWifiEnabled(ctx);
             return runAsShell(ACCESS_WIFI_STATE, () -> wm.isPortableHotspotSupported());
         } finally {
-            if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi disable");
+            if (!previousWifiEnabledState) {
+                new CtsNetUtils(ctx).disableWifi();
+            }
         }
     }
 
diff --git a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
index 244bfc5..11eb466 100644
--- a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
@@ -27,6 +27,7 @@
 import android.net.InetAddresses;
 import android.net.ipsec.ike.ChildSaProposal;
 import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
 import android.net.ipsec.ike.IkeIpv4AddrIdentification;
 import android.net.ipsec.ike.IkeIpv6AddrIdentification;
 import android.net.ipsec.ike.IkeSaProposal;
@@ -57,6 +58,11 @@
     }
 
     private static IkeSessionParams getTestIkeSessionParams(boolean testIpv6) {
+        return getTestIkeSessionParams(testIpv6, new IkeFqdnIdentification(TEST_IDENTITY));
+    }
+
+    public static IkeSessionParams getTestIkeSessionParams(boolean testIpv6,
+            IkeIdentification identification) {
         final String testServer = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
         final InetAddress addr = InetAddresses.parseNumericAddress(testServer);
         final IkeSessionParams.Builder ikeOptionsBuilder =
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 73e4c0e..26b058d 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -37,7 +37,7 @@
 import android.net.TestNetworkStackClient
 import android.net.Uri
 import android.net.metrics.IpConnectivityLog
-import android.net.util.MultinetworkPolicyTracker
+import com.android.server.connectivity.MultinetworkPolicyTracker
 import android.os.ConditionVariable
 import android.os.IBinder
 import android.os.SystemConfigManager
@@ -211,10 +211,15 @@
         doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
         doReturn(mock(BpfNetMaps::class.java)).`when`(deps).getBpfNetMaps(any(), any())
         doAnswer { inv ->
-            object : MultinetworkPolicyTracker(inv.getArgument(0), inv.getArgument(1),
-                    inv.getArgument(2)) {
-                override fun getResourcesForActiveSubId() = resources
-            }
+            MultinetworkPolicyTracker(inv.getArgument(0),
+                    inv.getArgument(1),
+                    inv.getArgument(2),
+                    object : MultinetworkPolicyTracker.Dependencies() {
+                        override fun getResourcesForActiveSubId(
+                            connResources: ConnectivityResources,
+                            activeSubId: Int
+                        ) = resources
+                    })
         }.`when`(deps).makeMultinetworkPolicyTracker(any(), any(), any())
         return deps
     }
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 97688d5..28edcb2 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -269,6 +269,7 @@
         mNetworkAgent.sendNetworkScore(score);
     }
 
+    // TODO : remove adjustScore and replace with the appropriate exiting flags.
     public void adjustScore(int change) {
         final int newLegacyScore = mScore.getLegacyInt() + change;
         final NetworkScore.Builder builder = new NetworkScore.Builder()
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index c7e8b97..aa5654a 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -100,13 +100,11 @@
     NETD "map_netd_uid_counterset_map",
     NETD "map_netd_uid_owner_map",
     NETD "map_netd_uid_permission_map",
-    SHARED "prog_clatd_schedcls_egress4_clat_ether",
     SHARED "prog_clatd_schedcls_egress4_clat_rawip",
     SHARED "prog_clatd_schedcls_ingress6_clat_ether",
     SHARED "prog_clatd_schedcls_ingress6_clat_rawip",
     NETD "prog_netd_cgroupskb_egress_stats",
     NETD "prog_netd_cgroupskb_ingress_stats",
-    NETD "prog_netd_cgroupsock_inet_create",
     NETD "prog_netd_schedact_ingress_account",
     NETD "prog_netd_skfilter_allowlist_xtbpf",
     NETD "prog_netd_skfilter_denylist_xtbpf",
@@ -114,6 +112,11 @@
     NETD "prog_netd_skfilter_ingress_xtbpf",
 };
 
+// Provided by *current* mainline module for T+ devices with 4.14+ kernels
+static const set<string> MAINLINE_FOR_T_4_14_PLUS = {
+    NETD "prog_netd_cgroupsock_inet_create",
+};
+
 // Provided by *current* mainline module for T+ devices with 5.4+ kernels
 static const set<string> MAINLINE_FOR_T_5_4_PLUS = {
     SHARED "prog_block_bind4_block_port",
@@ -152,6 +155,7 @@
     // Nothing added or removed in SCv2.
 
     DO_EXPECT(IsAtLeastT(), MAINLINE_FOR_T_PLUS);
+    DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
     DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_T_5_4_PLUS);
     DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
 }
diff --git a/tests/native/connectivity_native_test/Android.bp b/tests/native/connectivity_native_test/Android.bp
index 7d43aa8..8825aa4 100644
--- a/tests/native/connectivity_native_test/Android.bp
+++ b/tests/native/connectivity_native_test/Android.bp
@@ -10,7 +10,7 @@
         "vts",
     ],
     test_config_template: "AndroidTestTemplate.xml",
-    min_sdk_version: "31",
+    min_sdk_version: "34",
     tidy: false,
     srcs: [
         "connectivity_native_test.cpp",
diff --git a/tests/native/connectivity_native_test/connectivity_native_test.cpp b/tests/native/connectivity_native_test/connectivity_native_test.cpp
index 3db5265..27a9d35 100644
--- a/tests/native/connectivity_native_test/connectivity_native_test.cpp
+++ b/tests/native/connectivity_native_test/connectivity_native_test.cpp
@@ -14,62 +14,81 @@
  * limitations under the License.
  */
 
-#include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <android-modules-utils/sdk_level.h>
 #include <cutils/misc.h>  // FIRST_APPLICATION_UID
+#include <dlfcn.h>
 #include <gtest/gtest.h>
 #include <netinet/in.h>
 
 #include "bpf/BpfUtils.h"
 
-using aidl::android::net::connectivity::aidl::IConnectivityNative;
+typedef int (*GetPortsBlockedForBind)(in_port_t*, size_t*);
+GetPortsBlockedForBind getPortsBlockedForBind;
+typedef int (*BlockPortForBind)(in_port_t);
+BlockPortForBind blockPortForBind;
+typedef int (*UnblockPortForBind)(in_port_t);
+UnblockPortForBind unblockPortForBind;
+typedef int (*UnblockAllPortsForBind)();
+UnblockAllPortsForBind unblockAllPortsForBind;
 
 class ConnectivityNativeBinderTest : public ::testing::Test {
   public:
-    std::vector<int32_t> mActualBlockedPorts;
-
-    ConnectivityNativeBinderTest() {
-        AIBinder* binder = AServiceManager_getService("connectivity_native");
-        ndk::SpAIBinder sBinder = ndk::SpAIBinder(binder);
-        mService = aidl::android::net::connectivity::aidl::IConnectivityNative::fromBinder(sBinder);
-    }
+    in_port_t mActualBlockedPorts[65535];
+    size_t mActualBlockedPortsCount = 65535;
+    bool restoreBlockedPorts;
 
     void SetUp() override {
-        // Skip test case if not on T.
-        if (!android::modules::sdklevel::IsAtLeastT()) GTEST_SKIP() <<
+        restoreBlockedPorts = false;
+        // Skip test case if not on U.
+        if (!android::modules::sdklevel::IsAtLeastU()) GTEST_SKIP() <<
                 "Should be at least T device.";
 
         // Skip test case if not on 5.4 kernel which is required by bpf prog.
         if (!android::bpf::isAtLeastKernelVersion(5, 4, 0)) GTEST_SKIP() <<
                 "Kernel should be at least 5.4.";
 
-        ASSERT_NE(nullptr, mService.get());
+        // Necessary to use dlopen/dlsym since the lib is only available on U and there
+        // is no Sdk34ModuleController in tradefed yet.
+        // TODO: link against the library directly and add Sdk34ModuleController to
+        // AndroidTest.txml when available.
+        void* nativeLib = dlopen("libcom.android.tethering.connectivity_native.so", RTLD_NOW);
+        ASSERT_NE(nullptr, nativeLib);
+        getPortsBlockedForBind = reinterpret_cast<GetPortsBlockedForBind>(
+                dlsym(nativeLib, "AConnectivityNative_getPortsBlockedForBind"));
+        ASSERT_NE(nullptr, getPortsBlockedForBind);
+        blockPortForBind = reinterpret_cast<BlockPortForBind>(
+                dlsym(nativeLib, "AConnectivityNative_blockPortForBind"));
+        ASSERT_NE(nullptr, blockPortForBind);
+        unblockPortForBind = reinterpret_cast<UnblockPortForBind>(
+                dlsym(nativeLib, "AConnectivityNative_unblockPortForBind"));
+        ASSERT_NE(nullptr, unblockPortForBind);
+        unblockAllPortsForBind = reinterpret_cast<UnblockAllPortsForBind>(
+                dlsym(nativeLib, "AConnectivityNative_unblockAllPortsForBind"));
+        ASSERT_NE(nullptr, unblockAllPortsForBind);
 
         // If there are already ports being blocked on device unblockAllPortsForBind() store
         // the currently blocked ports and add them back at the end of the test. Do this for
         // every test case so additional test cases do not forget to add ports back.
-        ndk::ScopedAStatus status = mService->getPortsBlockedForBind(&mActualBlockedPorts);
-        EXPECT_TRUE(status.isOk()) << status.getDescription ();
-
+        int err = getPortsBlockedForBind(mActualBlockedPorts, &mActualBlockedPortsCount);
+        EXPECT_EQ(err, 0);
+        restoreBlockedPorts = true;
     }
 
     void TearDown() override {
-        ndk::ScopedAStatus status;
-        if (mActualBlockedPorts.size() > 0) {
-            for (int i : mActualBlockedPorts) {
-                mService->blockPortForBind(i);
-                EXPECT_TRUE(status.isOk()) << status.getDescription ();
+        int err;
+        if (mActualBlockedPortsCount > 0 && restoreBlockedPorts) {
+            for (int i=0; i < mActualBlockedPortsCount; i++) {
+                err = blockPortForBind(mActualBlockedPorts[i]);
+                EXPECT_EQ(err, 0);
             }
         }
     }
 
   protected:
-    std::shared_ptr<IConnectivityNative> mService;
-
     void runSocketTest (sa_family_t family, const int type, bool blockPort) {
-        ndk::ScopedAStatus status;
+        int err;
         in_port_t port = 0;
         int sock, sock2;
         // Open two sockets with SO_REUSEADDR and expect they can both bind to port.
@@ -79,16 +98,16 @@
         int blockedPort = 0;
         if (blockPort) {
             blockedPort = ntohs(port);
-            status = mService->blockPortForBind(blockedPort);
-            EXPECT_TRUE(status.isOk()) << status.getDescription ();
+            err = blockPortForBind(blockedPort);
+            EXPECT_EQ(err, 0);
         }
 
         int sock3 = openSocket(&port, family, type, blockPort /* expectBindFail */);
 
         if (blockPort) {
             EXPECT_EQ(-1, sock3);
-            status = mService->unblockPortForBind(blockedPort);
-            EXPECT_TRUE(status.isOk()) << status.getDescription ();
+            err = unblockPortForBind(blockedPort);
+            EXPECT_EQ(err, 0);
         } else {
             EXPECT_NE(-1, sock3);
         }
@@ -177,110 +196,74 @@
 }
 
 TEST_F(ConnectivityNativeBinderTest, BlockPortTwice) {
-    ndk::ScopedAStatus status = mService->blockPortForBind(5555);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    status = mService->blockPortForBind(5555);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    status = mService->unblockPortForBind(5555);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
+    int err = blockPortForBind(5555);
+    EXPECT_EQ(err, 0);
+    err = blockPortForBind(5555);
+    EXPECT_EQ(err, 0);
+    err = unblockPortForBind(5555);
+    EXPECT_EQ(err, 0);
 }
 
 TEST_F(ConnectivityNativeBinderTest, GetBlockedPorts) {
-    ndk::ScopedAStatus status;
-    std::vector<int> blockedPorts{1, 100, 1220, 1333, 2700, 5555, 5600, 65000};
-    for (int i : blockedPorts) {
-        status = mService->blockPortForBind(i);
-        EXPECT_TRUE(status.isOk()) << status.getDescription ();
+    int err;
+    in_port_t blockedPorts[8] = {1, 100, 1220, 1333, 2700, 5555, 5600, 65000};
+
+    if (mActualBlockedPortsCount > 0) {
+        err = unblockAllPortsForBind();
     }
-    std::vector<int32_t> actualBlockedPorts;
-    status = mService->getPortsBlockedForBind(&actualBlockedPorts);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    EXPECT_FALSE(actualBlockedPorts.empty());
-    EXPECT_EQ(blockedPorts, actualBlockedPorts);
+
+    for (int i : blockedPorts) {
+        err = blockPortForBind(i);
+        EXPECT_EQ(err, 0);
+    }
+    size_t actualBlockedPortsCount = 8;
+    in_port_t actualBlockedPorts[actualBlockedPortsCount];
+    err = getPortsBlockedForBind((in_port_t*) actualBlockedPorts, &actualBlockedPortsCount);
+    EXPECT_EQ(err, 0);
+    EXPECT_NE(actualBlockedPortsCount, 0);
+    for (int i=0; i < actualBlockedPortsCount; i++) {
+        EXPECT_EQ(blockedPorts[i], actualBlockedPorts[i]);
+    }
 
     // Remove the ports we added.
-    status = mService->unblockAllPortsForBind();
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    status = mService->getPortsBlockedForBind(&actualBlockedPorts);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    EXPECT_TRUE(actualBlockedPorts.empty());
+    err = unblockAllPortsForBind();
+    EXPECT_EQ(err, 0);
+    err = getPortsBlockedForBind(actualBlockedPorts, &actualBlockedPortsCount);
+    EXPECT_EQ(err, 0);
+    EXPECT_EQ(actualBlockedPortsCount, 0);
 }
 
 TEST_F(ConnectivityNativeBinderTest, UnblockAllPorts) {
-    ndk::ScopedAStatus status;
-    std::vector<int> blockedPorts{1, 100, 1220, 1333, 2700, 5555, 5600, 65000};
+    int err;
+    in_port_t blockedPorts[8] = {1, 100, 1220, 1333, 2700, 5555, 5600, 65000};
 
-    if (mActualBlockedPorts.size() > 0) {
-        status = mService->unblockAllPortsForBind();
+    if (mActualBlockedPortsCount > 0) {
+        err = unblockAllPortsForBind();
     }
 
     for (int i : blockedPorts) {
-        status = mService->blockPortForBind(i);
-        EXPECT_TRUE(status.isOk()) << status.getDescription ();
+        err = blockPortForBind(i);
+        EXPECT_EQ(err, 0);
     }
 
-    std::vector<int32_t> actualBlockedPorts;
-    status = mService->getPortsBlockedForBind(&actualBlockedPorts);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    EXPECT_FALSE(actualBlockedPorts.empty());
+    size_t actualBlockedPortsCount = 8;
+    in_port_t actualBlockedPorts[actualBlockedPortsCount];
+    err = getPortsBlockedForBind((in_port_t*) actualBlockedPorts, &actualBlockedPortsCount);
+    EXPECT_EQ(err, 0);
+    EXPECT_EQ(actualBlockedPortsCount, 8);
 
-    status = mService->unblockAllPortsForBind();
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    status = mService->getPortsBlockedForBind(&actualBlockedPorts);
-    EXPECT_TRUE(status.isOk()) << status.getDescription ();
-    EXPECT_TRUE(actualBlockedPorts.empty());
+    err = unblockAllPortsForBind();
+    EXPECT_EQ(err, 0);
+    err = getPortsBlockedForBind((in_port_t*) actualBlockedPorts, &actualBlockedPortsCount);
+    EXPECT_EQ(err, 0);
+    EXPECT_EQ(actualBlockedPortsCount, 0);
     // If mActualBlockedPorts is not empty, ports will be added back in teardown.
 }
 
-TEST_F(ConnectivityNativeBinderTest, BlockNegativePort) {
-    int retry = 0;
-    ndk::ScopedAStatus status;
-    do {
-        status = mService->blockPortForBind(-1);
-        // TODO: find out why transaction failed is being thrown on the first attempt.
-    } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5);
-    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
-}
-
-TEST_F(ConnectivityNativeBinderTest, UnblockNegativePort) {
-    int retry = 0;
-    ndk::ScopedAStatus status;
-    do {
-        status = mService->unblockPortForBind(-1);
-        // TODO: find out why transaction failed is being thrown on the first attempt.
-    } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5);
-    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
-}
-
-TEST_F(ConnectivityNativeBinderTest, BlockMaxPort) {
-    int retry = 0;
-    ndk::ScopedAStatus status;
-    do {
-        status = mService->blockPortForBind(65536);
-        // TODO: find out why transaction failed is being thrown on the first attempt.
-    } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5);
-    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
-}
-
-TEST_F(ConnectivityNativeBinderTest, UnblockMaxPort) {
-    int retry = 0;
-    ndk::ScopedAStatus status;
-    do {
-        status = mService->unblockPortForBind(65536);
-        // TODO: find out why transaction failed is being thrown on the first attempt.
-    } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5);
-    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, status.getExceptionCode());
-}
-
 TEST_F(ConnectivityNativeBinderTest, CheckPermission) {
-    int retry = 0;
     int curUid = getuid();
     EXPECT_EQ(0, seteuid(FIRST_APPLICATION_UID + 2000)) << "seteuid failed: " << strerror(errno);
-    ndk::ScopedAStatus status;
-    do {
-        status = mService->blockPortForBind(5555);
-        // TODO: find out why transaction failed is being thrown on the first attempt.
-    } while (status.getExceptionCode() == EX_TRANSACTION_FAILED && retry++ < 5);
-    EXPECT_EQ(EX_SECURITY, status.getExceptionCode());
+    int err = blockPortForBind((in_port_t) 5555);
+    EXPECT_EQ(EPERM, err);
     EXPECT_EQ(0, seteuid(curUid)) << "seteuid failed: " << strerror(errno);
 }
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index cb68235..8ed735a 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -66,7 +66,6 @@
         "java/android/net/netstats/NetworkStatsDataMigrationUtilsTest.kt",
         "java/com/android/internal/net/NetworkUtilsInternalTest.java",
         "java/com/android/internal/net/VpnProfileTest.java",
-        "java/com/android/server/NetworkManagementServiceTest.java",
         "java/com/android/server/VpnManagerServiceTest.java",
         "java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
         "java/com/android/server/connectivity/IpConnectivityMetricsTest.java",
@@ -101,7 +100,7 @@
     ],
     static_libs: [
         "androidx.test.rules",
-        "androidx.test.uiautomator",
+        "androidx.test.uiautomator_uiautomator",
         "bouncycastle-repackaged-unbundled",
         "core-tests-support",
         "FrameworksNetCommonTests",
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 54e1cd0..5bac2dd 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -50,6 +50,15 @@
     <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
     <uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" />
 
+    <!-- Declare the intent that the test intends to query. This is necessary for
+         UiDevice.getLauncherPackageName which is used in NetworkNotificationManagerTest
+    -->
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.MAIN" />
+            <category android:name="android.intent.category.HOME"/>
+        </intent>
+    </queries>
     <application android:testOnly="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="android.net.ipsec.ike" />
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 8a537be..679427a 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -16,7 +16,16 @@
 
 package android.app.usage;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 
@@ -34,7 +43,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.NetworkStats.Entry;
@@ -86,31 +94,17 @@
         final int uid2 = 10002;
         final int uid3 = 10003;
 
-        Entry uid1Entry1 = new Entry("if1", uid1,
-                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
-                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
-                android.net.NetworkStats.DEFAULT_NETWORK_NO,
-                100, 10, 200, 20, 0);
+        Entry uid1Entry1 = new Entry("if1", uid1, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 100, 10, 200, 20, 0);
 
-        Entry uid1Entry2 = new Entry(
-                "if2", uid1,
-                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
-                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
-                android.net.NetworkStats.DEFAULT_NETWORK_NO,
-                100, 10, 200, 20, 0);
+        Entry uid1Entry2 = new Entry("if2", uid1, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 100, 10, 200, 20, 0);
 
-        Entry uid2Entry1 = new Entry("if1", uid2,
-                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
-                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
-                android.net.NetworkStats.DEFAULT_NETWORK_NO,
-                150, 10, 250, 20, 0);
+        Entry uid2Entry1 = new Entry("if1", uid2, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 150, 10, 250, 20, 0);
 
-        Entry uid2Entry2 = new Entry(
-                "if2", uid2,
-                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
-                android.net.NetworkStats.METERED_NO, android.net.NetworkStats.ROAMING_NO,
-                android.net.NetworkStats.DEFAULT_NETWORK_NO,
-                150, 10, 250, 20, 0);
+        Entry uid2Entry2 = new Entry("if2", uid2, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 150, 10, 250, 20, 0);
 
         NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
         history1.recordData(10, 20, uid1Entry1);
@@ -125,9 +119,8 @@
         when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 });
 
         when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
-                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
-                eq(android.net.NetworkStats.TAG_NONE),
-                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+                eq(uid1), eq(SET_ALL), eq(TAG_NONE),
+                eq(FIELD_ALL), eq(startTime), eq(endTime)))
                 .then((InvocationOnMock inv) -> {
                     NetworkTemplate template = inv.getArgument(0);
                     assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
@@ -136,9 +129,8 @@
                 });
 
         when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
-                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
-                eq(android.net.NetworkStats.TAG_NONE),
-                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+                eq(uid2), eq(SET_ALL), eq(TAG_NONE),
+                eq(FIELD_ALL), eq(startTime), eq(endTime)))
                 .then((InvocationOnMock inv) -> {
                     NetworkTemplate template = inv.getArgument(0);
                     assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
@@ -148,7 +140,7 @@
 
 
         NetworkStats stats = mManager.queryDetails(
-                ConnectivityManager.TYPE_MOBILE, TEST_SUBSCRIBER_ID, startTime, endTime);
+                TYPE_MOBILE, TEST_SUBSCRIBER_ID, startTime, endTime);
 
         NetworkStats.Bucket bucket = new NetworkStats.Bucket();
 
@@ -202,35 +194,34 @@
 
         verify(mStatsSession, times(1)).getHistoryIntervalForUid(
                 eq(expectedTemplate),
-                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
-                eq(android.net.NetworkStats.TAG_NONE),
-                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+                eq(uid1), eq(SET_ALL),
+                eq(TAG_NONE),
+                eq(FIELD_ALL), eq(startTime), eq(endTime));
 
         verify(mStatsSession, times(1)).getHistoryIntervalForUid(
                 eq(expectedTemplate),
-                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
-                eq(android.net.NetworkStats.TAG_NONE),
-                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+                eq(uid2), eq(SET_ALL),
+                eq(TAG_NONE),
+                eq(FIELD_ALL), eq(startTime), eq(endTime));
 
         assertFalse(stats.hasNextBucket());
     }
 
     @Test
     public void testNetworkTemplateWhenRunningQueryDetails_NoSubscriberId() throws RemoteException {
-        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_MOBILE,
-                null /* subscriberId */, new NetworkTemplate.Builder(MATCH_MOBILE)
-                        .setMeteredness(METERED_YES).build());
-        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
-                "" /* subscriberId */, new NetworkTemplate.Builder(MATCH_WIFI).build());
-        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
-                null /* subscriberId */, new NetworkTemplate.Builder(MATCH_WIFI).build());
+        runQueryDetailsAndCheckTemplate(TYPE_MOBILE, null /* subscriberId */,
+                new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build());
+        runQueryDetailsAndCheckTemplate(TYPE_WIFI, "" /* subscriberId */,
+                new NetworkTemplate.Builder(MATCH_WIFI).build());
+        runQueryDetailsAndCheckTemplate(TYPE_WIFI, null /* subscriberId */,
+                new NetworkTemplate.Builder(MATCH_WIFI).build());
     }
 
     @Test
     public void testNetworkTemplateWhenRunningQueryDetails_MergedCarrierWifi()
             throws RemoteException {
-        runQueryDetailsAndCheckTemplate(ConnectivityManager.TYPE_WIFI,
-                TEST_SUBSCRIBER_ID, new NetworkTemplate.Builder(MATCH_WIFI)
+        runQueryDetailsAndCheckTemplate(TYPE_WIFI, TEST_SUBSCRIBER_ID,
+                new NetworkTemplate.Builder(MATCH_WIFI)
                         .setSubscriberIds(Set.of(TEST_SUBSCRIBER_ID)).build());
     }
 
@@ -244,7 +235,7 @@
         when(mStatsSession.getTaggedSummaryForAllUid(any(NetworkTemplate.class),
                 anyLong(), anyLong()))
                 .thenReturn(new android.net.NetworkStats(0, 0));
-        final NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+        final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE)
                 .setMeteredness(NetworkStats.Bucket.METERED_YES).build();
         NetworkStats stats = mManager.queryTaggedSummary(template, startTime, endTime);
 
@@ -265,12 +256,12 @@
         when(mStatsSession.getHistoryIntervalForNetwork(any(NetworkTemplate.class),
                 anyInt(), anyLong(), anyLong()))
                 .thenReturn(new NetworkStatsHistory(10, 0));
-        final NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+        final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE)
                 .setMeteredness(NetworkStats.Bucket.METERED_YES).build();
         NetworkStats stats = mManager.queryDetailsForDevice(template, startTime, endTime);
 
         verify(mStatsSession, times(1)).getHistoryIntervalForNetwork(
-                eq(template), eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+                eq(template), eq(FIELD_ALL), eq(startTime), eq(endTime));
 
         assertFalse(stats.hasNextBucket());
     }
diff --git a/tests/unit/java/android/net/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index c327868..45a9dbc 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -498,7 +498,7 @@
     public void testConnectivityManagerDoesNotLeakContext() throws Exception {
         final WeakReference<Context> ref = makeConnectivityManagerAndReturnContext();
 
-        final int attempts = 100;
+        final int attempts = 600;
         final long waitIntervalMs = 50;
         for (int i = 0; i < attempts; i++) {
             forceGC();
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 2f5b0ab..3b68120 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
 import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
+import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;
 
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
@@ -28,6 +29,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.ipsec.ike.IkeKeyIdIdentification;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.os.Build;
 import android.test.mock.MockContext;
@@ -446,6 +448,40 @@
     }
 
     @Test
+    public void testBuildWithIkeTunConnParamsConvertToVpnProfile() throws Exception {
+        // Special keyId that contains delimiter character of VpnProfile
+        final byte[] keyId = "foo\0bar".getBytes();
+        final IkeTunnelConnectionParams tunnelParams = new IkeTunnelConnectionParams(
+                getTestIkeSessionParams(true /* testIpv6 */, new IkeKeyIdIdentification(keyId)),
+                CHILD_PARAMS);
+        final Ikev2VpnProfile ikev2VpnProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+        final VpnProfile vpnProfile = ikev2VpnProfile.toVpnProfile();
+
+        assertEquals(VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS, vpnProfile.type);
+
+        // Username, password, server, ipsecIdentifier, ipsecCaCert, ipsecSecret, ipsecUserCert and
+        // getAllowedAlgorithms should not be set if IkeTunnelConnectionParams is set.
+        assertEquals("", vpnProfile.server);
+        assertEquals("", vpnProfile.ipsecIdentifier);
+        assertEquals("", vpnProfile.username);
+        assertEquals("", vpnProfile.password);
+        assertEquals("", vpnProfile.ipsecCaCert);
+        assertEquals("", vpnProfile.ipsecSecret);
+        assertEquals("", vpnProfile.ipsecUserCert);
+        assertEquals(0, vpnProfile.getAllowedAlgorithms().size());
+
+        // IkeTunnelConnectionParams should stay the same.
+        assertEquals(tunnelParams, vpnProfile.ikeTunConnParams);
+
+        // Convert to disk-stable format and then back to Ikev2VpnProfile should be the same.
+        final VpnProfile decodedVpnProfile =
+                VpnProfile.decode(vpnProfile.key, vpnProfile.encode());
+        final Ikev2VpnProfile convertedIkev2VpnProfile =
+                Ikev2VpnProfile.fromVpnProfile(decodedVpnProfile);
+        assertEquals(ikev2VpnProfile, convertedIkev2VpnProfile);
+    }
+
+    @Test
     public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
         final IkeTunnelConnectionParams tunnelParams =
                 new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
diff --git a/tests/unit/java/android/net/IpSecAlgorithmTest.java b/tests/unit/java/android/net/IpSecAlgorithmTest.java
index 1482055..54ad961 100644
--- a/tests/unit/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/unit/java/android/net/IpSecAlgorithmTest.java
@@ -27,6 +27,7 @@
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Parcel;
+import android.os.SystemProperties;
 
 import androidx.test.filters.SmallTest;
 
@@ -123,9 +124,7 @@
 
     @Test
     public void testValidationForAlgosAddedInS() throws Exception {
-        if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
-            return;
-        }
+        if (SystemProperties.getInt("ro.vendor.api_level", 10000) <= Build.VERSION_CODES.R) return;
 
         for (int len : new int[] {160, 224, 288}) {
             checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
@@ -194,15 +193,17 @@
     }
 
     private static Set<String> getMandatoryAlgos() {
+        int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", 10000);
         return CollectionUtils.filter(
                 ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
-                i -> Build.VERSION.DEVICE_INITIAL_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+                i -> vendorApiLevel >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
     }
 
     private static Set<String> getOptionalAlgos() {
+        int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level", 10000);
         return CollectionUtils.filter(
                 ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
-                i -> Build.VERSION.DEVICE_INITIAL_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+                i -> vendorApiLevel < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
     }
 
     @Test
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index d84328c..9667f8f 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -101,15 +101,15 @@
                 false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
         assertFalse(netIdent2.isMetered())
 
-        // Verify network is not metered because it has NET_CAPABILITY_TEMPORARILY_NOT_METERED
-        // capability .
+        // In current design, a network that has NET_CAPABILITY_TEMPORARILY_NOT_METERED
+        // capability will be treated as metered.
         val capsTempNotMetered = NetworkCapabilities().apply {
             setCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED, true)
         }
         val netIdent3 = NetworkIdentity.buildNetworkIdentity(mockContext,
                 buildMobileNetworkStateSnapshot(capsTempNotMetered, TEST_IMSI1),
                 false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
-        assertFalse(netIdent3.isMetered())
+        assertTrue(netIdent3.isMetered())
     }
 
     @Test
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index 709b722..126ad55 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -1067,6 +1067,38 @@
         }
     }
 
+    @Test
+    public void testClearInterfaces() {
+        final NetworkStats stats = new NetworkStats(TEST_START, 1);
+        final NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+
+        final NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "test2", 10101, SET_DEFAULT, 0xF0DD, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+
+        stats.insertEntry(entry1);
+        stats.insertEntry(entry2);
+
+        // Verify that the interfaces have indeed been recorded.
+        assertEquals(2, stats.size());
+        assertValues(stats, 0, "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+        assertValues(stats, 1, "test2", 10101, SET_DEFAULT, 0xF0DD, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+
+        // Clear interfaces.
+        stats.clearInterfaces();
+
+        // Verify that the interfaces are cleared.
+        assertEquals(2, stats.size());
+        assertValues(stats, 0, null /* iface */, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 50L, 100L, 20L, 0L);
+        assertValues(stats, 1, null /* iface */, 10101, SET_DEFAULT, 0xF0DD, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 51200, 25L, 101010L, 50L, 0L);
+    }
+
     private static void assertContains(NetworkStats stats,  String iface, int uid, int set,
             int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
             long txBytes, long txPackets, long operations) {
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 6c39169..3cf0228 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -32,6 +32,7 @@
 import android.net.NetworkStats.METERED_NO
 import android.net.NetworkStats.METERED_YES
 import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_CARRIER
 import android.net.NetworkTemplate.MATCH_MOBILE
 import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
 import android.net.NetworkTemplate.MATCH_TEST
@@ -41,12 +42,8 @@
 import android.net.NetworkTemplate.OEM_MANAGED_ALL
 import android.net.NetworkTemplate.OEM_MANAGED_NO
 import android.net.NetworkTemplate.OEM_MANAGED_YES
-import android.net.NetworkTemplate.WIFI_NETWORK_KEY_ALL
-import android.net.NetworkTemplate.buildTemplateCarrierMetered
 import android.net.NetworkTemplate.buildTemplateMobileAll
 import android.net.NetworkTemplate.buildTemplateMobileWildcard
-import android.net.NetworkTemplate.buildTemplateMobileWithRatType
-import android.net.NetworkTemplate.buildTemplateWifi
 import android.net.NetworkTemplate.buildTemplateWifiWildcard
 import android.net.NetworkTemplate.normalize
 import android.net.wifi.WifiInfo
@@ -144,10 +141,16 @@
 
     @Test
     fun testWifiMatches() {
-        val templateWifiKey1 = buildTemplateWifi(TEST_WIFI_KEY1)
-        val templateWifiKey1ImsiNull = buildTemplateWifi(TEST_WIFI_KEY1, null)
-        val templateWifiKey1Imsi1 = buildTemplateWifi(TEST_WIFI_KEY1, TEST_IMSI1)
-        val templateWifiKeyAllImsi1 = buildTemplateWifi(WIFI_NETWORK_KEY_ALL, TEST_IMSI1)
+        val templateWifiKey1 = NetworkTemplate.Builder(MATCH_WIFI)
+                .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
+        val templateWifiKey1ImsiNull = NetworkTemplate.Builder(MATCH_WIFI)
+                .setSubscriberIds(setOf(null))
+                .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
+        val templateWifiKey1Imsi1 = NetworkTemplate.Builder(MATCH_WIFI)
+                .setSubscriberIds(setOf(TEST_IMSI1))
+                .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
+        val templateWifiKeyAllImsi1 = NetworkTemplate.Builder(MATCH_WIFI)
+                .setSubscriberIds(setOf(TEST_IMSI1)).build()
 
         val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1),
                 false, TelephonyManager.NETWORK_TYPE_UMTS)
@@ -196,8 +199,10 @@
     @Test
     fun testMobileMatches() {
         val templateMobileImsi1 = buildTemplateMobileAll(TEST_IMSI1)
-        val templateMobileImsi2WithRatType = buildTemplateMobileWithRatType(TEST_IMSI2,
-                TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
+        val templateMobileImsi2WithRatType = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_YES)
+                .setSubscriberIds(setOf(TEST_IMSI2))
+                .setRatType(TelephonyManager.NETWORK_TYPE_UMTS).build()
 
         val mobileImsi1 = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* wifiKey */,
                 OEM_NONE, true /* metered */)
@@ -225,16 +230,21 @@
     @Test
     fun testMobileWildcardMatches() {
         val templateMobileWildcard = buildTemplateMobileWildcard()
-        val templateMobileNullImsiWithRatType = buildTemplateMobileWithRatType(null,
-                TelephonyManager.NETWORK_TYPE_UMTS, METERED_ALL)
+        val templateMobileNullImsiWithRatType = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setRatType(TelephonyManager.NETWORK_TYPE_UMTS).build()
 
         val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
         val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
                 false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+        val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
+        val identMobile2 = buildNetworkIdentity(mockContext, mobileImsi2,
+                false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_LTE)
 
         // Verify that the template matches any subscriberId.
         templateMobileWildcard.assertMatches(identMobile1)
         templateMobileNullImsiWithRatType.assertMatches(identMobile1)
+        templateMobileWildcard.assertMatches(identMobile2)
+        templateMobileNullImsiWithRatType.assertDoesNotMatch(identMobile2)
 
         val identWifiImsi1Key1 = buildNetworkIdentity(
                 mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_WIFI_KEY1), true, 0)
@@ -272,7 +282,9 @@
 
     @Test
     fun testCarrierMeteredMatches() {
-        val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1)
+        val templateCarrierImsi1Metered = NetworkTemplate.Builder(MATCH_CARRIER)
+                .setMeteredness(METERED_YES)
+                .setSubscriberIds(setOf(TEST_IMSI1)).build()
 
         val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
         val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1,
@@ -317,24 +329,25 @@
 
         // Build UMTS template that matches mobile identities with RAT in the same
         // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
-        val templateUmtsMetered = buildTemplateMobileWithRatType(null,
-                TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
+        val templateUmtsMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_YES)
+                .setRatType(TelephonyManager.NETWORK_TYPE_UMTS).build()
         // Build normal template that matches mobile identities with any RAT and IMSI.
-        val templateAllMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
-                METERED_YES)
+        val templateAllMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_YES).build()
         // Build template with UNKNOWN RAT that matches mobile identities with RAT that
         // cannot be determined.
-        val templateUnknownMetered =
-                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                METERED_YES)
-
-        val templateUmtsNonMetered = buildTemplateMobileWithRatType(null,
-                TelephonyManager.NETWORK_TYPE_UMTS, METERED_NO)
-        val templateAllNonMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
-                METERED_NO)
-        val templateUnknownNonMetered =
-                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                METERED_NO)
+        val templateUnknownMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_YES)
+                .setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN).build()
+        val templateUmtsNonMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_NO)
+                .setRatType(TelephonyManager.NETWORK_TYPE_UMTS).build()
+        val templateAllNonMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_NO).build()
+        val templateUnknownNonMetered = NetworkTemplate.Builder(MATCH_MOBILE)
+                .setMeteredness(METERED_NO)
+                .setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN).build()
 
         val identUmtsMetered = buildNetworkIdentity(
                 mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_UMTS)
@@ -435,15 +448,15 @@
 
     @Test
     fun testParcelUnparcel() {
-        val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null,
-                arrayOf<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+        val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, emptyArray<String>(),
+                emptyArray<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
                 TelephonyManager.NETWORK_TYPE_LTE, OEM_MANAGED_ALL,
                 SUBSCRIBER_ID_MATCH_RULE_EXACT)
-        val templateWifi = NetworkTemplate(MATCH_WIFI, null, null,
+        val templateWifi = NetworkTemplate(MATCH_WIFI, null, emptyArray<String>(),
                 arrayOf(TEST_WIFI_KEY1), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, 0,
                 OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
-        val templateOem = NetworkTemplate(MATCH_MOBILE, null, null,
-                arrayOf<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, 0,
+        val templateOem = NetworkTemplate(MATCH_MOBILE, null, emptyArray<String>(),
+                emptyArray<String>(), METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, 0,
                 OEM_MANAGED_YES, SUBSCRIBER_ID_MATCH_RULE_EXACT)
         assertParcelSane(templateMobile, 10)
         assertParcelSane(templateWifi, 10)
@@ -548,7 +561,7 @@
 
     @Test
     fun testNormalize() {
-        var mergedImsiList = listOf(arrayOf(TEST_IMSI1, TEST_IMSI2))
+        var mergedImsiList = arrayOf(TEST_IMSI1, TEST_IMSI2)
         val identMobileImsi1 = buildNetworkIdentity(mockContext,
                 buildMobileNetworkState(TEST_IMSI1), false /* defaultNetwork */,
                 TelephonyManager.NETWORK_TYPE_UMTS)
@@ -570,12 +583,18 @@
             it.assertMatches(identMobileImsi2)
             it.assertDoesNotMatch(identMobileImsi3)
         }
-        normalize(buildTemplateCarrierMetered(TEST_IMSI1), mergedImsiList).also {
+        val templateCarrierImsi1 = NetworkTemplate.Builder(MATCH_CARRIER)
+                .setMeteredness(METERED_YES)
+                .setSubscriberIds(setOf(TEST_IMSI1)).build()
+        normalize(templateCarrierImsi1, mergedImsiList).also {
             it.assertMatches(identMobileImsi1)
             it.assertMatches(identMobileImsi2)
             it.assertDoesNotMatch(identMobileImsi3)
         }
-        normalize(buildTemplateWifi(TEST_WIFI_KEY1, TEST_IMSI1), mergedImsiList).also {
+        val templateWifiKey1Imsi1 = NetworkTemplate.Builder(MATCH_WIFI)
+                .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1))
+                .setSubscriberIds(setOf(TEST_IMSI1)).build()
+        normalize(templateWifiKey1Imsi1, mergedImsiList).also {
             it.assertMatches(identWifiImsi1Key1)
             it.assertMatches(identWifiImsi2Key1)
             it.assertDoesNotMatch(identWifiImsi3WifiKey1)
diff --git a/tests/unit/java/android/net/VpnTransportInfoTest.java b/tests/unit/java/android/net/VpnTransportInfoTest.java
deleted file mode 100644
index b4c7ac4..0000000
--- a/tests/unit/java/android/net/VpnTransportInfoTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 android.net;
-
-import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
-import static android.net.NetworkCapabilities.REDACT_NONE;
-
-import static com.android.testutils.ParcelUtils.assertParcelSane;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(DevSdkIgnoreRunner.class)
-@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-public class VpnTransportInfoTest {
-
-    @Test
-    public void testParceling() {
-        VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
-        assertParcelSane(v, 2 /* fieldCount */);
-    }
-
-    @Test
-    public void testEqualsAndHashCode() {
-        String session1 = "12345";
-        String session2 = "6789";
-        VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
-        VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
-        VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
-        VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
-        VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
-        VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
-
-        VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
-        VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
-
-        assertNotEquals(v11, v12);
-        assertNotEquals(v13, v14);
-        assertNotEquals(v14, v15);
-        assertNotEquals(v14, v21);
-
-        assertEquals(v11, v13);
-        assertEquals(v31, v32);
-        assertEquals(v11.hashCode(), v13.hashCode());
-        assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
-        assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId());
-    }
-}
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index 15a2e56..0e17cd7 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -30,16 +30,23 @@
 import static android.net.INetd.PERMISSION_NONE;
 import static android.net.INetd.PERMISSION_UNINSTALLED;
 import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
+import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.EPERM;
 
 import static com.android.server.BpfNetMaps.DOZABLE_MATCH;
 import static com.android.server.BpfNetMaps.HAPPY_BOX_MATCH;
 import static com.android.server.BpfNetMaps.IIF_MATCH;
 import static com.android.server.BpfNetMaps.LOCKDOWN_VPN_MATCH;
+import static com.android.server.BpfNetMaps.LOW_POWER_STANDBY_MATCH;
 import static com.android.server.BpfNetMaps.NO_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_1_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_2_MATCH;
+import static com.android.server.BpfNetMaps.OEM_DENY_3_MATCH;
 import static com.android.server.BpfNetMaps.PENALTY_BOX_MATCH;
 import static com.android.server.BpfNetMaps.POWERSAVE_MATCH;
 import static com.android.server.BpfNetMaps.RESTRICTED_MATCH;
+import static com.android.server.BpfNetMaps.STANDBY_MATCH;
+import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -47,20 +54,29 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.StatsManager;
 import android.content.Context;
 import android.net.INetd;
 import android.os.Build;
 import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
+import android.util.IndentingPrintWriter;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.Struct.U32;
 import com.android.net.module.util.Struct.U8;
+import com.android.net.module.util.bpf.CookieTagMapKey;
+import com.android.net.module.util.bpf.CookieTagMapValue;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -74,6 +90,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.FileDescriptor;
+import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(DevSdkIgnoreRunner.class)
@@ -92,8 +111,8 @@
     private static final int NO_IIF = 0;
     private static final int NULL_IIF = 0;
     private static final String CHAINNAME = "fw_dozable";
-    private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
-    private static final U32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new U32(1);
+    private static final S32 UID_RULES_CONFIGURATION_KEY = new S32(0);
+    private static final S32 CURRENT_STATS_MAP_CONFIGURATION_KEY = new S32(1);
     private static final List<Integer> FIREWALL_CHAINS = List.of(
             FIREWALL_CHAIN_DOZABLE,
             FIREWALL_CHAIN_STANDBY,
@@ -113,10 +132,12 @@
     @Mock INetd mNetd;
     @Mock BpfNetMaps.Dependencies mDeps;
     @Mock Context mContext;
-    private final IBpfMap<U32, U32> mConfigurationMap = new TestBpfMap<>(U32.class, U32.class);
-    private final IBpfMap<U32, UidOwnerValue> mUidOwnerMap =
-            new TestBpfMap<>(U32.class, UidOwnerValue.class);
-    private final IBpfMap<U32, U8> mUidPermissionMap = new TestBpfMap<>(U32.class, U8.class);
+    private final IBpfMap<S32, U32> mConfigurationMap = new TestBpfMap<>(S32.class, U32.class);
+    private final IBpfMap<S32, UidOwnerValue> mUidOwnerMap =
+            new TestBpfMap<>(S32.class, UidOwnerValue.class);
+    private final IBpfMap<S32, U8> mUidPermissionMap = new TestBpfMap<>(S32.class, U8.class);
+    private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap =
+            spy(new TestBpfMap<>(CookieTagMapKey.class, CookieTagMapValue.class));
 
     @Before
     public void setUp() throws Exception {
@@ -125,8 +146,12 @@
         doReturn(0).when(mDeps).synchronizeKernelRCU();
         BpfNetMaps.setEnableJavaBpfMapForTest(true /* enable */);
         BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
+        mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
+        mConfigurationMap.updateEntry(
+                CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_A));
         BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
         BpfNetMaps.setUidPermissionMapForTest(mUidPermissionMap);
+        BpfNetMaps.setCookieTagMapForTest(mCookieTagMap);
         mBpfNetMaps = new BpfNetMaps(mContext, mNetd, mDeps);
     }
 
@@ -279,9 +304,9 @@
                 () -> mBpfNetMaps.setChildChain(FIREWALL_CHAIN_DOZABLE, true /* enable */));
     }
 
-    private void checkUidOwnerValue(final long uid, final long expectedIif,
+    private void checkUidOwnerValue(final int uid, final int expectedIif,
             final long expectedMatch) throws Exception {
-        final UidOwnerValue config = mUidOwnerMap.getValue(new U32(uid));
+        final UidOwnerValue config = mUidOwnerMap.getValue(new S32(uid));
         if (expectedMatch == 0) {
             assertNull(config);
         } else {
@@ -290,8 +315,8 @@
         }
     }
 
-    private void doTestRemoveNaughtyApp(final long iif, final long match) throws Exception {
-        mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+    private void doTestRemoveNaughtyApp(final int iif, final long match) throws Exception {
+        mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
 
         mBpfNetMaps.removeNaughtyApp(TEST_UID);
 
@@ -328,9 +353,9 @@
                 () -> mBpfNetMaps.removeNaughtyApp(TEST_UID));
     }
 
-    private void doTestAddNaughtyApp(final long iif, final long match) throws Exception {
+    private void doTestAddNaughtyApp(final int iif, final long match) throws Exception {
         if (match != NO_MATCH) {
-            mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+            mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
         }
 
         mBpfNetMaps.addNaughtyApp(TEST_UID);
@@ -360,8 +385,8 @@
                 () -> mBpfNetMaps.addNaughtyApp(TEST_UID));
     }
 
-    private void doTestRemoveNiceApp(final long iif, final long match) throws Exception {
-        mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+    private void doTestRemoveNiceApp(final int iif, final long match) throws Exception {
+        mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
 
         mBpfNetMaps.removeNiceApp(TEST_UID);
 
@@ -398,9 +423,9 @@
                 () -> mBpfNetMaps.removeNiceApp(TEST_UID));
     }
 
-    private void doTestAddNiceApp(final long iif, final long match) throws Exception {
+    private void doTestAddNiceApp(final int iif, final long match) throws Exception {
         if (match != NO_MATCH) {
-            mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+            mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
         }
 
         mBpfNetMaps.addNiceApp(TEST_UID);
@@ -430,10 +455,10 @@
                 () -> mBpfNetMaps.addNiceApp(TEST_UID));
     }
 
-    private void doTestUpdateUidLockdownRule(final long iif, final long match, final boolean add)
+    private void doTestUpdateUidLockdownRule(final int iif, final long match, final boolean add)
             throws Exception {
         if (match != NO_MATCH) {
-            mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+            mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
         }
 
         mBpfNetMaps.updateUidLockdownRule(TEST_UID, add);
@@ -503,8 +528,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = DOZABLE_MATCH;
         final long match1 = DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(NO_IIF, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NO_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(NO_IIF, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NO_IIF, match1));
 
         mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
 
@@ -519,8 +544,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = IIF_MATCH;
         final long match1 = IIF_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX + 1, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(TEST_IF_INDEX + 1, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NULL_IIF, match1));
 
         mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
 
@@ -543,8 +568,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = IIF_MATCH;
         final long match1 = IIF_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NULL_IIF, match1));
 
         mBpfNetMaps.addUidInterfaceRules(null /* ifName */, TEST_UIDS);
 
@@ -552,12 +577,12 @@
         checkUidOwnerValue(uid1, NULL_IIF, match1);
     }
 
-    private void doTestRemoveUidInterfaceRules(final long iif0, final long match0,
-            final long iif1, final long match1) throws Exception {
+    private void doTestRemoveUidInterfaceRules(final int iif0, final long match0,
+            final int iif1, final long match1) throws Exception {
         final int uid0 = TEST_UIDS[0];
         final int uid1 = TEST_UIDS[1];
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(iif0, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(iif1, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(iif0, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(iif1, match1));
 
         mBpfNetMaps.removeUidInterfaceRules(TEST_UIDS);
 
@@ -580,7 +605,7 @@
     }
 
     private void doTestSetUidRule(final List<Integer> testChains) throws Exception {
-        mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(TEST_IF_INDEX, IIF_MATCH));
+        mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(TEST_IF_INDEX, IIF_MATCH));
 
         for (final int chain: testChains) {
             final int ruleToAddMatch = mBpfNetMaps.isFirewallAllowList(chain)
@@ -684,8 +709,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = POWERSAVE_MATCH;
         final long match1 = POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(NO_IIF, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NO_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(NO_IIF, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NO_IIF, match1));
 
         mBpfNetMaps.replaceUidChain(FIREWALL_CHAIN_DOZABLE, new int[]{uid1});
 
@@ -700,8 +725,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = IIF_MATCH;
         final long match1 = IIF_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NULL_IIF, match1));
 
         mBpfNetMaps.replaceUidChain(FIREWALL_CHAIN_DOZABLE, TEST_UIDS);
 
@@ -716,8 +741,8 @@
         final int uid1 = TEST_UIDS[1];
         final long match0 = IIF_MATCH | DOZABLE_MATCH;
         final long match1 = IIF_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
-        mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
-        mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+        mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
+        mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NULL_IIF, match1));
 
         mBpfNetMaps.replaceUidChain(FIREWALL_CHAIN_DOZABLE, new int[]{uid1});
 
@@ -755,8 +780,8 @@
 
         final int uid0 = TEST_UIDS[0];
         final int uid1 = TEST_UIDS[1];
-        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new S32(uid1)).val);
     }
 
     @Test
@@ -767,8 +792,8 @@
 
         final int uid0 = TEST_UIDS[0];
         final int uid1 = TEST_UIDS[1];
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid1)).val);
     }
 
     @Test
@@ -779,8 +804,8 @@
         mBpfNetMaps.setNetPermForUids(PERMISSION_INTERNET, TEST_UIDS);
         mBpfNetMaps.setNetPermForUids(PERMISSION_NONE, new int[]{uid0});
 
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertNull(mUidPermissionMap.getValue(new U32(uid1)));
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertNull(mUidPermissionMap.getValue(new S32(uid1)));
     }
 
     @Test
@@ -791,8 +816,8 @@
         mBpfNetMaps.setNetPermForUids(PERMISSION_UPDATE_DEVICE_STATS, TEST_UIDS);
         mBpfNetMaps.setNetPermForUids(PERMISSION_NONE, new int[]{uid0});
 
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(PERMISSION_UPDATE_DEVICE_STATS, mUidPermissionMap.getValue(new S32(uid1)).val);
     }
 
     @Test
@@ -804,8 +829,8 @@
         mBpfNetMaps.setNetPermForUids(permission, TEST_UIDS);
         mBpfNetMaps.setNetPermForUids(PERMISSION_NONE, new int[]{uid0});
 
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid1)).val);
     }
 
     @Test
@@ -817,8 +842,8 @@
         mBpfNetMaps.setNetPermForUids(permission, TEST_UIDS);
         mBpfNetMaps.setNetPermForUids(PERMISSION_UNINSTALLED, new int[]{uid0});
 
-        assertNull(mUidPermissionMap.getValue(new U32(uid0)));
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertNull(mUidPermissionMap.getValue(new S32(uid0)));
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid1)).val);
     }
 
     @Test
@@ -829,28 +854,28 @@
         final int permission = PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS;
 
         mBpfNetMaps.setNetPermForUids(permission, TEST_UIDS);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid1)).val);
 
         mBpfNetMaps.setNetPermForUids(permission, TEST_UIDS);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(permission, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(permission, mUidPermissionMap.getValue(new S32(uid1)).val);
 
         mBpfNetMaps.setNetPermForUids(PERMISSION_NONE, TEST_UIDS);
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid1)).val);
 
         mBpfNetMaps.setNetPermForUids(PERMISSION_NONE, TEST_UIDS);
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid0)).val);
-        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new U32(uid1)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid0)).val);
+        assertEquals(PERMISSION_NONE, mUidPermissionMap.getValue(new S32(uid1)).val);
 
         mBpfNetMaps.setNetPermForUids(PERMISSION_UNINSTALLED, TEST_UIDS);
-        assertNull(mUidPermissionMap.getValue(new U32(uid0)));
-        assertNull(mUidPermissionMap.getValue(new U32(uid1)));
+        assertNull(mUidPermissionMap.getValue(new S32(uid0)));
+        assertNull(mUidPermissionMap.getValue(new S32(uid1)));
 
         mBpfNetMaps.setNetPermForUids(PERMISSION_UNINSTALLED, TEST_UIDS);
-        assertNull(mUidPermissionMap.getValue(new U32(uid0)));
-        assertNull(mUidPermissionMap.getValue(new U32(uid1)));
+        assertNull(mUidPermissionMap.getValue(new S32(uid0)));
+        assertNull(mUidPermissionMap.getValue(new S32(uid1)));
     }
 
     @Test
@@ -877,4 +902,179 @@
 
         assertThrows(ServiceSpecificException.class, () -> mBpfNetMaps.swapActiveStatsMap());
     }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testPullBpfMapInfo() throws Exception {
+        // mCookieTagMap has 1 entry
+        mCookieTagMap.updateEntry(new CookieTagMapKey(0), new CookieTagMapValue(0, 0));
+
+        // mUidOwnerMap has 2 entries
+        mUidOwnerMap.updateEntry(new S32(0), new UidOwnerValue(0, 0));
+        mUidOwnerMap.updateEntry(new S32(1), new UidOwnerValue(0, 0));
+
+        // mUidPermissionMap has 3 entries
+        mUidPermissionMap.updateEntry(new S32(0), new U8((short) 0));
+        mUidPermissionMap.updateEntry(new S32(1), new U8((short) 0));
+        mUidPermissionMap.updateEntry(new S32(2), new U8((short) 0));
+
+        final int ret = mBpfNetMaps.pullBpfMapInfoAtom(NETWORK_BPF_MAP_INFO, new ArrayList<>());
+        assertEquals(StatsManager.PULL_SUCCESS, ret);
+        verify(mDeps).buildStatsEvent(
+                1 /* cookieTagMapSize */, 2 /* uidOwnerMapSize */, 3 /* uidPermissionMapSize */);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testPullBpfMapInfoGetMapSizeFailure() throws Exception {
+        doThrow(new ErrnoException("", EINVAL)).when(mCookieTagMap).forEach(any());
+        final int ret = mBpfNetMaps.pullBpfMapInfoAtom(NETWORK_BPF_MAP_INFO, new ArrayList<>());
+        assertEquals(StatsManager.PULL_SKIP, ret);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testPullBpfMapInfoUnexpectedAtomTag() {
+        final int ret = mBpfNetMaps.pullBpfMapInfoAtom(-1 /* atomTag */, new ArrayList<>());
+        assertEquals(StatsManager.PULL_SKIP, ret);
+    }
+
+    private void assertDumpContains(final String dump, final String message) {
+        assertTrue(String.format("dump(%s) does not contain '%s'", dump, message),
+                dump.contains(message));
+    }
+
+    private String getDump() throws Exception {
+        final StringWriter sw = new StringWriter();
+        mBpfNetMaps.dump(new IndentingPrintWriter(sw), new FileDescriptor(), true /* verbose */);
+        return sw.toString();
+    }
+
+    private void doTestDumpUidPermissionMap(final int permission, final String permissionString)
+            throws Exception {
+        mUidPermissionMap.updateEntry(new S32(TEST_UID), new U8((short) permission));
+        assertDumpContains(getDump(), TEST_UID + " " + permissionString);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidPermissionMap() throws Exception {
+        doTestDumpUidPermissionMap(PERMISSION_NONE, "PERMISSION_NONE");
+        doTestDumpUidPermissionMap(PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
+                "PERMISSION_INTERNET PERMISSION_UPDATE_DEVICE_STATS");
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidPermissionMapInvalidPermission() throws Exception {
+        doTestDumpUidPermissionMap(PERMISSION_UNINSTALLED, "PERMISSION_UNINSTALLED error!");
+        doTestDumpUidPermissionMap(PERMISSION_INTERNET | 1 << 6,
+                "PERMISSION_INTERNET PERMISSION_UNKNOWN(64)");
+    }
+
+    void doTestDumpUidOwnerMap(final int iif, final long match, final String matchString)
+            throws Exception {
+        mUidOwnerMap.updateEntry(new S32(TEST_UID), new UidOwnerValue(iif, match));
+        assertDumpContains(getDump(), TEST_UID + " " + matchString);
+    }
+
+    void doTestDumpUidOwnerMap(final long match, final String matchString) throws Exception {
+        doTestDumpUidOwnerMap(0 /* iif */, match, matchString);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidOwnerMap() throws Exception {
+        doTestDumpUidOwnerMap(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
+        doTestDumpUidOwnerMap(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
+        doTestDumpUidOwnerMap(DOZABLE_MATCH, "DOZABLE_MATCH");
+        doTestDumpUidOwnerMap(STANDBY_MATCH, "STANDBY_MATCH");
+        doTestDumpUidOwnerMap(POWERSAVE_MATCH, "POWERSAVE_MATCH");
+        doTestDumpUidOwnerMap(RESTRICTED_MATCH, "RESTRICTED_MATCH");
+        doTestDumpUidOwnerMap(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH");
+        doTestDumpUidOwnerMap(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH");
+        doTestDumpUidOwnerMap(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH");
+        doTestDumpUidOwnerMap(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH");
+        doTestDumpUidOwnerMap(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH");
+
+        doTestDumpUidOwnerMap(HAPPY_BOX_MATCH | POWERSAVE_MATCH,
+                "HAPPY_BOX_MATCH POWERSAVE_MATCH");
+        doTestDumpUidOwnerMap(DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+                "DOZABLE_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH");
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidOwnerMapWithIifMatch() throws Exception {
+        doTestDumpUidOwnerMap(TEST_IF_INDEX, IIF_MATCH, "IIF_MATCH " + TEST_IF_INDEX);
+        doTestDumpUidOwnerMap(TEST_IF_INDEX,
+                IIF_MATCH | DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+                "DOZABLE_MATCH IIF_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH " + TEST_IF_INDEX);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidOwnerMapWithInvalidMatch() throws Exception {
+        final long invalid_match = 1L << 31;
+        doTestDumpUidOwnerMap(invalid_match, "UNKNOWN_MATCH(" + invalid_match + ")");
+        doTestDumpUidOwnerMap(DOZABLE_MATCH | invalid_match,
+                "DOZABLE_MATCH UNKNOWN_MATCH(" + invalid_match + ")");
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpCurrentStatsMapConfig() throws Exception {
+        mConfigurationMap.updateEntry(
+                CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_A));
+        assertDumpContains(getDump(), "current statsMap configuration: 0 SELECT_MAP_A");
+
+        mConfigurationMap.updateEntry(
+                CURRENT_STATS_MAP_CONFIGURATION_KEY, new U32(STATS_SELECT_MAP_B));
+        assertDumpContains(getDump(), "current statsMap configuration: 1 SELECT_MAP_B");
+    }
+
+    private void doTestDumpOwnerMatchConfig(final long match, final String matchString)
+            throws Exception {
+        mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(match));
+        assertDumpContains(getDump(),
+                "current ownerMatch configuration: " + match + " " + matchString);
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidOwnerMapConfig() throws Exception {
+        doTestDumpOwnerMatchConfig(HAPPY_BOX_MATCH, "HAPPY_BOX_MATCH");
+        doTestDumpOwnerMatchConfig(PENALTY_BOX_MATCH, "PENALTY_BOX_MATCH");
+        doTestDumpOwnerMatchConfig(DOZABLE_MATCH, "DOZABLE_MATCH");
+        doTestDumpOwnerMatchConfig(STANDBY_MATCH, "STANDBY_MATCH");
+        doTestDumpOwnerMatchConfig(POWERSAVE_MATCH, "POWERSAVE_MATCH");
+        doTestDumpOwnerMatchConfig(RESTRICTED_MATCH, "RESTRICTED_MATCH");
+        doTestDumpOwnerMatchConfig(LOW_POWER_STANDBY_MATCH, "LOW_POWER_STANDBY_MATCH");
+        doTestDumpOwnerMatchConfig(IIF_MATCH, "IIF_MATCH");
+        doTestDumpOwnerMatchConfig(LOCKDOWN_VPN_MATCH, "LOCKDOWN_VPN_MATCH");
+        doTestDumpOwnerMatchConfig(OEM_DENY_1_MATCH, "OEM_DENY_1_MATCH");
+        doTestDumpOwnerMatchConfig(OEM_DENY_2_MATCH, "OEM_DENY_2_MATCH");
+        doTestDumpOwnerMatchConfig(OEM_DENY_3_MATCH, "OEM_DENY_3_MATCH");
+
+        doTestDumpOwnerMatchConfig(HAPPY_BOX_MATCH | POWERSAVE_MATCH,
+                "HAPPY_BOX_MATCH POWERSAVE_MATCH");
+        doTestDumpOwnerMatchConfig(DOZABLE_MATCH | LOCKDOWN_VPN_MATCH | OEM_DENY_1_MATCH,
+                "DOZABLE_MATCH LOCKDOWN_VPN_MATCH OEM_DENY_1_MATCH");
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpUidOwnerMapConfigWithInvalidMatch() throws Exception {
+        final long invalid_match = 1L << 31;
+        doTestDumpOwnerMatchConfig(invalid_match, "UNKNOWN_MATCH(" + invalid_match + ")");
+        doTestDumpOwnerMatchConfig(DOZABLE_MATCH | invalid_match,
+                "DOZABLE_MATCH UNKNOWN_MATCH(" + invalid_match + ")");
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+    public void testDumpCookieTagMap() throws Exception {
+        mCookieTagMap.updateEntry(new CookieTagMapKey(123), new CookieTagMapValue(456, 0x789));
+        assertDumpContains(getDump(), "cookie=123 tag=0x789 uid=456");
+    }
 }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index e23e72d..67cc7bd 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -302,7 +302,6 @@
 import android.net.resolv.aidl.Nat64PrefixEventParcel;
 import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
 import android.net.shared.PrivateDnsConfig;
-import android.net.util.MultinetworkPolicyTracker;
 import android.net.wifi.WifiInfo;
 import android.os.BadParcelableException;
 import android.os.BatteryStatsManager;
@@ -357,17 +356,19 @@
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkMonitorUtils;
+import com.android.networkstack.apishim.ConstantsShim;
 import com.android.networkstack.apishim.NetworkAgentConfigShimImpl;
-import com.android.networkstack.apishim.api29.ConstantsShim;
 import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.ConnectivityService.NetworkRequestInfo;
 import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
 import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
 import com.android.server.connectivity.ClatCoordinator;
 import com.android.server.connectivity.ConnectivityFlags;
-import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.MultinetworkPolicyTracker;
+import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
+import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
@@ -440,8 +441,6 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
-import kotlin.reflect.KClass;
-
 /**
  * Tests for {@link ConnectivityService}.
  *
@@ -1067,38 +1066,41 @@
          * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
          */
         public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
-            ConnectivityManager.NetworkCallback callback = null;
             final ConditionVariable validatedCv = new ConditionVariable();
+            final ConditionVariable capsChangedCv = new ConditionVariable();
+            final NetworkRequest request = new NetworkRequest.Builder()
+                    .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
+                    .clearCapabilities()
+                    .build();
             if (validated) {
                 setNetworkValid(isStrictMode);
-                NetworkRequest request = new NetworkRequest.Builder()
-                        .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
-                        .clearCapabilities()
-                        .build();
-                callback = new ConnectivityManager.NetworkCallback() {
-                    public void onCapabilitiesChanged(Network network,
-                            NetworkCapabilities networkCapabilities) {
-                        if (network.equals(getNetwork()) &&
-                                networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+            }
+            final NetworkCallback callback = new NetworkCallback() {
+                public void onCapabilitiesChanged(Network network,
+                        NetworkCapabilities networkCapabilities) {
+                    if (network.equals(getNetwork())) {
+                        capsChangedCv.open();
+                        if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
                             validatedCv.open();
                         }
                     }
-                };
-                mCm.registerNetworkCallback(request, callback);
-            }
+                }
+            };
+            mCm.registerNetworkCallback(request, callback);
+
             if (hasInternet) {
                 addCapability(NET_CAPABILITY_INTERNET);
             }
 
             connectWithoutInternet();
+            waitFor(capsChangedCv);
 
             if (validated) {
                 // Wait for network to validate.
                 waitFor(validatedCv);
                 setNetworkInvalid(isStrictMode);
             }
-
-            if (callback != null) mCm.unregisterNetworkCallback(callback);
+            mCm.unregisterNetworkCallback(callback);
         }
 
         public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
@@ -1169,10 +1171,11 @@
         void setNetworkPartialValid(boolean isStrictMode) {
             setNetworkPartial();
             mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID;
+            mNmValidationRedirectUrl = null;
             int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
                     | NETWORK_VALIDATION_PROBE_HTTP;
             int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP;
-            // Suppose the partial network cannot pass the private DNS validation as well, so only
+            // Assume the partial network cannot pass the private DNS validation as well, so only
             // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
             if (isStrictMode) {
                 probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
@@ -1632,12 +1635,7 @@
         volatile int mConfigMeteredMultipathPreference;
 
         WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
-            super(c, h, r);
-        }
-
-        @Override
-        protected Resources getResourcesForActiveSubId() {
-            return mResources;
+            super(c, h, r, new MultinetworkPolicyTrackerTestDependencies(mResources));
         }
 
         @Override
@@ -1836,30 +1834,20 @@
                 .getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any());
         doReturn(R.array.network_switch_type_name).when(mResources)
                 .getIdentifier(eq("network_switch_type_name"), eq("array"), any());
-        doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
-                .getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
         doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+        doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
         doReturn(true).when(mResources)
                 .getBoolean(R.bool.config_cellular_radio_timesharing_capable);
     }
 
     class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
         final ConnectivityResources mConnRes;
-        @Mock final MockableSystemProperties mSystemProperties;
 
         ConnectivityServiceDependencies(final Context mockResContext) {
-            mSystemProperties = mock(MockableSystemProperties.class);
-            doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
-
             mConnRes = new ConnectivityResources(mockResContext);
         }
 
         @Override
-        public MockableSystemProperties getSystemProperties() {
-            return mSystemProperties;
-        }
-
-        @Override
         public HandlerThread makeHandlerThread() {
             return mCsHandlerThread;
         }
@@ -2348,7 +2336,7 @@
         b = registerConnectivityBroadcast(1);
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.requestNetwork(legacyRequest, callback);
-        callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        callback.expect(CallbackEntry.AVAILABLE, mCellNetworkAgent);
         mCm.unregisterNetworkCallback(callback);
         b.expectNoBroadcast(800);  // 800ms long enough to at least flake if this is sent
 
@@ -2420,7 +2408,7 @@
         // is added in case of flakiness.
         final int nascentTimeoutMs =
                 mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
-        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+        listenCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
 
         // 2. Create a network that is satisfied by a request comes later.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2436,7 +2424,7 @@
         // to get disconnected as usual if the request is released after the nascent timer expires.
         listenCallback.assertNoCallback(nascentTimeoutMs);
         mCm.unregisterNetworkCallback(wifiCallback);
-        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        listenCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // 3. Create a network that is satisfied by a request comes later.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2447,7 +2435,7 @@
 
         // Verify that the network will still be torn down after the request gets removed.
         mCm.unregisterNetworkCallback(wifiCallback);
-        listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        listenCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // There is no need to ensure that LOSING is never sent in the common case that the
         // network immediately satisfies a request that was already present, because it is already
@@ -2493,7 +2481,7 @@
         assertFalse(isForegroundNetwork(mCellNetworkAgent));
 
         mCellNetworkAgent.disconnect();
-        bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        bgMobileListenCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         fgMobileListenCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(wifiListenCallback);
@@ -2701,7 +2689,7 @@
         final TestNetworkCallback generalCb = new TestNetworkCallback();
         final TestNetworkCallback defaultCb = new TestNetworkCallback();
         mCm.registerNetworkCallback(
-                new NetworkRequest.Builder().addTransportType(transport | transport).build(),
+                new NetworkRequest.Builder().addTransportType(transport).build(),
                 generalCb);
         mCm.registerDefaultNetworkCallback(defaultCb);
 
@@ -2721,7 +2709,7 @@
         // Make sure the default request goes to net 2
         generalCb.expectAvailableCallbacksUnvalidated(net2);
         if (expectLingering) {
-            generalCb.expectCallback(CallbackEntry.LOSING, net1);
+            generalCb.expectLosing(net1);
         }
         generalCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, net2);
         defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
@@ -2738,7 +2726,7 @@
             net1.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
         }
         net1.disconnect();
-        generalCb.expectCallback(CallbackEntry.LOST, net1);
+        generalCb.expect(CallbackEntry.LOST, net1);
 
         // Remove primary from net 2
         net2.setScore(new NetworkScore.Builder().build());
@@ -2766,7 +2754,7 @@
             // get LOSING. If the radio can't time share, this is a hard loss, since the last
             // request keeping up this network has been removed and the network isn't lingering
             // for any other request.
-            generalCb.expectCallback(CallbackEntry.LOSING, net2);
+            generalCb.expectLosing(net2);
             net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS);
             generalCb.assertNoCallback();
             net2.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
@@ -2774,7 +2762,7 @@
             net2.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
         }
         net2.disconnect();
-        generalCb.expectCallback(CallbackEntry.LOST, net2);
+        generalCb.expect(CallbackEntry.LOST, net2);
         defaultCb.assertNoCallback();
 
         net3.disconnect();
@@ -2956,13 +2944,14 @@
 
     @Test
     public void testRequiresValidation() {
-        assertTrue(NetworkMonitorUtils.isValidationRequired(false /* isVpnValidationRequired */,
+        assertTrue(NetworkMonitorUtils.isValidationRequired(false /* isDunValidationRequired */,
+                false /* isVpnValidationRequired */,
                 mCm.getDefaultRequest().networkCapabilities));
     }
 
     /**
      * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks
-     * this class receives, by calling expectCallback() exactly once each time a callback is
+     * this class receives, by calling expect() exactly once each time a callback is
      * received. assertNoCallback may be called at any time.
      */
     private class TestNetworkCallback extends TestableNetworkCallback {
@@ -2977,20 +2966,24 @@
             assertNoCallback(0 /* timeout */);
         }
 
-        @Override
-        public <T extends CallbackEntry> T expectCallback(final KClass<T> type, final HasNetwork n,
-                final long timeoutMs) {
-            final T callback = super.expectCallback(type, n, timeoutMs);
-            if (callback instanceof CallbackEntry.Losing) {
-                // TODO : move this to the specific test(s) needing this rather than here.
-                final CallbackEntry.Losing losing = (CallbackEntry.Losing) callback;
-                final int maxMsToLive = losing.getMaxMsToLive();
-                String msg = String.format(
-                        "Invalid linger time value %d, must be between %d and %d",
-                        maxMsToLive, 0, mService.mLingerDelayMs);
-                assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs);
+        public CallbackEntry.Losing expectLosing(final HasNetwork n, final long timeoutMs) {
+            final CallbackEntry.Losing losing = expect(CallbackEntry.LOSING, n, timeoutMs);
+            final int maxMsToLive = losing.getMaxMsToLive();
+            if (maxMsToLive < 0 || maxMsToLive > mService.mLingerDelayMs) {
+                // maxMsToLive is the value that was received in the onLosing callback. That must
+                // not be negative, so check that.
+                // Also, maxMsToLive is the remaining time until the network expires.
+                // mService.mLingerDelayMs is how long the network takes from when it's first
+                // detected to be unneeded to when it expires, so maxMsToLive should never
+                // be greater than that.
+                fail(String.format("Invalid linger time value %d, must be between %d and %d",
+                        maxMsToLive, 0, mService.mLingerDelayMs));
             }
-            return callback;
+            return losing;
+        }
+
+        public CallbackEntry.Losing expectLosing(final HasNetwork n) {
+            return expectLosing(n, getDefaultTimeoutMs());
         }
     }
 
@@ -3004,19 +2997,19 @@
 
     static void expectOnLost(TestNetworkAgentWrapper network, TestNetworkCallback ... callbacks) {
         for (TestNetworkCallback c : callbacks) {
-            c.expectCallback(CallbackEntry.LOST, network);
+            c.expect(CallbackEntry.LOST, network);
         }
     }
 
     static void expectAvailableCallbacksUnvalidatedWithSpecifier(TestNetworkAgentWrapper network,
             NetworkSpecifier specifier, TestNetworkCallback ... callbacks) {
         for (TestNetworkCallback c : callbacks) {
-            c.expectCallback(CallbackEntry.AVAILABLE, network);
+            c.expect(CallbackEntry.AVAILABLE, network);
             c.expectCapabilitiesThat(network, (nc) ->
                     !nc.hasCapability(NET_CAPABILITY_VALIDATED)
                             && Objects.equals(specifier, nc.getNetworkSpecifier()));
-            c.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, network);
-            c.expectCallback(CallbackEntry.BLOCKED_STATUS, network);
+            c.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, network);
+            c.expect(CallbackEntry.BLOCKED_STATUS, network);
         }
     }
 
@@ -3099,16 +3092,16 @@
 
         b = registerConnectivityBroadcast(2);
         mWiFiNetworkAgent.disconnect();
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        wifiNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         b.expectBroadcast();
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
         b = registerConnectivityBroadcast(1);
         mCellNetworkAgent.disconnect();
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         b.expectBroadcast();
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
@@ -3129,21 +3122,21 @@
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        genericNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        genericNetworkCallback.expectLosing(mCellNetworkAgent);
         genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        cellNetworkCallback.expectLosing(mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
         mWiFiNetworkAgent.disconnect();
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        wifiNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
         mCellNetworkAgent.disconnect();
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
     }
 
@@ -3276,7 +3269,7 @@
         // We then get LOSING when wifi validates and cell is outscored.
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -3285,15 +3278,15 @@
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
         // TODO: Investigate sending validated before losing.
-        callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+        callback.expectLosing(mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         mEthernetNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -3309,7 +3302,7 @@
                 newNetwork = mWiFiNetworkAgent;
 
             }
-            callback.expectCallback(CallbackEntry.LOSING, oldNetwork);
+            callback.expectLosing(oldNetwork);
             // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
             // longer lingering?
             defaultCallback.expectAvailableCallbacksValidated(newNetwork);
@@ -3323,7 +3316,7 @@
         // We expect a notification about the capabilities change, and nothing else.
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
         defaultCallback.assertNoCallback();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Wifi no longer satisfies our listen, which is for an unmetered network.
@@ -3332,11 +3325,11 @@
 
         // Disconnect our test networks.
         mWiFiNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
         mCellNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
 
@@ -3367,8 +3360,8 @@
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -3379,19 +3372,19 @@
         mWiFiNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
 
@@ -3406,7 +3399,7 @@
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         // TODO: Investigate sending validated before losing.
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -3417,13 +3410,13 @@
         // TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
         // lingering?
         mCm.unregisterNetworkCallback(noopCallback);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
 
         // Similar to the above: lingering can start even after the lingered request is removed.
         // Disconnect wifi and switch to cell.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -3442,12 +3435,12 @@
         callback.assertNoCallback();
         // Now unregister cellRequest and expect cell to start lingering.
         mCm.unregisterNetworkCallback(noopCallback);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
 
         // Let linger run its course.
         callback.assertNoCallback();
         final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
-        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs);
+        callback.expect(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs);
 
         // Register a TRACK_DEFAULT request and check that it does not affect lingering.
         TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
@@ -3456,20 +3449,20 @@
         mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+        callback.expectLosing(mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
         trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         // Let linger run its course.
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
 
         // Clean up.
         mEthernetNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
-        trackDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
+        trackDefaultCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
 
         mCm.unregisterNetworkCallback(callback);
         mCm.unregisterNetworkCallback(defaultCallback);
@@ -3484,7 +3477,12 @@
             final int uid, final String packageName) throws Exception {
         doReturn(buildPackageInfo(true /* hasSystemPermission */, uid)).when(mPackageManager)
                 .getPackageInfo(eq(packageName), eq(GET_PERMISSIONS));
-        mService.mPermissionMonitor.onPackageAdded(packageName, uid);
+
+        // Send a broadcast indicating a package was installed.
+        final Intent addedIntent = new Intent(ACTION_PACKAGE_ADDED);
+        addedIntent.putExtra(Intent.EXTRA_UID, uid);
+        addedIntent.setData(Uri.parse("package:" + packageName));
+        processBroadcast(addedIntent);
     }
 
     @Test
@@ -3511,7 +3509,7 @@
         mWiFiNetworkAgent.connect(true);
         defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
 
         // File a request for cellular, then release it.
@@ -3520,7 +3518,7 @@
         NetworkCallback noopCallback = new NetworkCallback();
         mCm.requestNetwork(cellRequest, noopCallback);
         mCm.unregisterNetworkCallback(noopCallback);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
 
         // Let linger run its course.
         callback.assertNoCallback();
@@ -3533,6 +3531,58 @@
         mCm.unregisterNetworkCallback(callback);
     }
 
+    /** Expects the specified notification and returns the notification ID. */
+    private int expectNotification(TestNetworkAgentWrapper agent, NotificationType type) {
+        verify(mNotificationManager, timeout(TIMEOUT_MS)).notify(
+                eq(NetworkNotificationManager.tagFor(agent.getNetwork().netId)),
+                eq(type.eventId), any());
+        return type.eventId;
+    }
+
+    private void expectNoNotification(@NonNull final TestNetworkAgentWrapper agent) {
+        verify(mNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any());
+    }
+
+    /**
+     * Expects the specified notification happens when the unvalidated prompt message arrives
+     *
+     * @return the notification ID.
+     **/
+    private int expectUnvalidationCheckWillNotify(TestNetworkAgentWrapper agent,
+            NotificationType type) {
+        mService.scheduleEvaluationTimeout(agent.getNetwork(), 0 /* delayMs */);
+        waitForIdle();
+        return expectNotification(agent, type);
+    }
+
+    /**
+     * Expects that the notification for the specified network is cleared.
+     *
+     * This generally happens when the network disconnects or when the newtwork validates. During
+     * normal usage the notification is also cleared by the system when the notification is tapped.
+     */
+    private void expectClearNotification(TestNetworkAgentWrapper agent, NotificationType type) {
+        verify(mNotificationManager, timeout(TIMEOUT_MS)).cancel(
+                eq(NetworkNotificationManager.tagFor(agent.getNetwork().netId)), eq(type.eventId));
+    }
+
+    /**
+     * Expects that no notification happens when the unvalidated prompt message arrives
+     *
+     * @return the notification ID.
+     **/
+    private void expectUnvalidationCheckWillNotNotify(TestNetworkAgentWrapper agent) {
+        mService.scheduleEvaluationTimeout(agent.getNetwork(), 0 /*delayMs */);
+        waitForIdle();
+        expectNoNotification(agent);
+    }
+
+    private void expectDisconnectAndClearNotifications(TestNetworkCallback callback,
+            TestNetworkAgentWrapper agent, NotificationType type) {
+        callback.expect(CallbackEntry.LOST, agent);
+        expectClearNotification(agent, type);
+    }
+
     private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) {
         return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission,
                 /*secure=*/ false, VpnManager.TYPE_VPN_NONE, /*excludeLocalRoutes=*/ false);
@@ -3653,10 +3703,13 @@
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
 
-        // Cell Remains the default.
+        // Cell remains the default.
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
-        // Lower wifi's score to below than cell, and check that it doesn't disconnect because
+        // Expect a high-priority NO_INTERNET notification.
+        expectUnvalidationCheckWillNotify(mWiFiNetworkAgent, NotificationType.NO_INTERNET);
+
+        // Lower WiFi's score to lower than cell, and check that it doesn't disconnect because
         // it's explicitly selected.
         mWiFiNetworkAgent.adjustScore(-40);
         mWiFiNetworkAgent.adjustScore(40);
@@ -3665,35 +3718,44 @@
         // If the user chooses yes on the "No Internet access, stay connected?" dialog, we switch to
         // wifi even though it's unvalidated.
         mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), true, false);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
         // Disconnect wifi, and then reconnect, again with explicitlySelected=true.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        expectDisconnectAndClearNotifications(callback, mWiFiNetworkAgent,
+                NotificationType.NO_INTERNET);
+
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, false);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
 
+        // Expect a high-priority NO_INTERNET notification.
+        expectUnvalidationCheckWillNotify(mWiFiNetworkAgent, NotificationType.NO_INTERNET);
+
         // If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
         // network to disconnect.
         mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), false, false);
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        expectDisconnectAndClearNotifications(callback, mWiFiNetworkAgent,
+                NotificationType.NO_INTERNET);
+        reset(mNotificationManager);
 
         // Reconnect, again with explicitlySelected=true, but this time validate.
+        // Expect no notifications.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, false);
         mWiFiNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
 
         mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetNetworkAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+        callback.expectLosing(mWiFiNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
         assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.assertNoCallback();
@@ -3702,32 +3764,92 @@
         // (i.e., with explicitlySelected=true and acceptUnvalidated=true). Expect to switch to
         // wifi immediately.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(true, true);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mEthernetNetworkAgent);
+        callback.expectLosing(mEthernetNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         mEthernetNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
 
         // Disconnect and reconnect with explicitlySelected=false and acceptUnvalidated=true.
         // Check that the network is not scored specially and that the device prefers cell data.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.explicitlySelected(false, true);
         mWiFiNetworkAgent.connect(false);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
 
         // Clean up.
         mWiFiNetworkAgent.disconnect();
         mCellNetworkAgent.disconnect();
 
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+    }
+
+    private void doTestFirstEvaluation(
+            @NonNull final Consumer<TestNetworkAgentWrapper> doConnect,
+            final boolean waitForSecondCaps,
+            final boolean evaluatedByValidation)
+            throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .build();
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        doConnect.accept(mWiFiNetworkAgent);
+        // Expect the available callbacks, but don't require specific values for their arguments
+        // since this method doesn't know how the network was connected.
+        callback.expect(CallbackEntry.AVAILABLE, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.BLOCKED_STATUS, mWiFiNetworkAgent);
+        if (waitForSecondCaps) {
+            // This is necessary because of b/245893397, the same bug that happens where we use
+            // expectAvailableDoubleValidatedCallbacks.
+            callback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
+        }
+        final NetworkAgentInfo nai =
+                mService.getNetworkAgentInfoForNetwork(mWiFiNetworkAgent.getNetwork());
+        final long firstEvaluation = nai.getFirstEvaluationConcludedTime();
+        if (evaluatedByValidation) {
+            assertNotEquals(0L, firstEvaluation);
+        } else {
+            assertEquals(0L, firstEvaluation);
+        }
+        mService.scheduleEvaluationTimeout(mWiFiNetworkAgent.getNetwork(), 0L /* timeout */);
+        waitForIdle();
+        if (evaluatedByValidation) {
+            assertEquals(firstEvaluation, nai.getFirstEvaluationConcludedTime());
+        } else {
+            assertNotEquals(0L, nai.getFirstEvaluationConcludedTime());
+        }
+        mWiFiNetworkAgent.disconnect();
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+        mCm.unregisterNetworkCallback(callback);
+    }
+
+    @Test
+    public void testEverEvaluated() throws Exception {
+        doTestFirstEvaluation(naw -> naw.connect(true /* validated */),
+                true /* waitForSecondCaps */, true /* immediatelyEvaluated */);
+        doTestFirstEvaluation(naw -> naw.connectWithPartialConnectivity(),
+                true /* waitForSecondCaps */, true /* immediatelyEvaluated */);
+        doTestFirstEvaluation(naw -> naw.connectWithCaptivePortal(TEST_REDIRECT_URL, false),
+                true /* waitForSecondCaps */, true /* immediatelyEvaluated */);
+        doTestFirstEvaluation(naw -> naw.connect(false /* validated */),
+                false /* waitForSecondCaps */, false /* immediatelyEvaluated */);
     }
 
     private void tryNetworkFactoryRequests(int capability) throws Exception {
@@ -4088,6 +4210,12 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.assertNoCallback();
 
+        // Expect a PARTIAL_CONNECTIVITY notification. The notification appears as soon as partial
+        // connectivity is detected, and is low priority because the network was not explicitly
+        // selected by the user. This happens if we reconnect to a network where the user previously
+        // accepted partial connectivity without checking "always".
+        expectNotification(mWiFiNetworkAgent, NotificationType.PARTIAL_CONNECTIVITY);
+
         // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
         // probe.
         mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
@@ -4100,18 +4228,22 @@
         waitForIdle();
         verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
 
-        // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
+        // Need a trigger point to let NetworkMonitor tell ConnectivityService that the network is
         // validated.
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED,
                 mWiFiNetworkAgent);
         assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
 
+        // Once the network validates, the notification disappears.
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.PARTIAL_CONNECTIVITY);
+
         // Disconnect and reconnect wifi with partial connectivity again.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connectWithPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -4119,34 +4251,42 @@
 
         // Mobile data should be the default network.
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        waitForIdle();
+
+        // Expect a low-priority PARTIAL_CONNECTIVITY notification as soon as partial connectivity
+        // is detected.
+        expectNotification(mWiFiNetworkAgent, NotificationType.PARTIAL_CONNECTIVITY);
 
         // If the user chooses no, disconnect wifi immediately.
-        mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */,
+        mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false /* accept */,
                 false /* always */);
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.PARTIAL_CONNECTIVITY);
+        reset(mNotificationManager);
 
-        // If user accepted partial connectivity before, and device reconnects to that network
-        // again, but now the network has full connectivity. The network shouldn't contain
+        // If the user accepted partial connectivity before, and the device connects to that network
+        // again, but now the network has full connectivity, then the network shouldn't contain
         // NET_CAPABILITY_PARTIAL_CONNECTIVITY.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         // acceptUnvalidated is also used as setting for accepting partial networks.
         mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
                 true /* acceptUnvalidated */);
         mWiFiNetworkAgent.connect(true);
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
 
         // If user accepted partial connectivity network before,
         // NetworkMonitor#setAcceptPartialConnectivity() will be called in
         // ConnectivityService#updateNetworkInfo().
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
 
         // Wifi should be the default network.
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // The user accepted partial connectivity and selected "don't ask again". Now the user
         // reconnects to the partial connectivity network. Switch to wifi as soon as partial
@@ -4160,17 +4300,19 @@
         // ConnectivityService#updateNetworkInfo().
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
+
         mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
 
-        // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
+        // Need a trigger point to let NetworkMonitor tell ConnectivityService that the network is
         // validated.
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // If the user accepted partial connectivity, and the device auto-reconnects to the partial
         // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED.
@@ -4184,19 +4326,22 @@
         mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */);
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(
                 NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        verifyNoMoreInteractions(mNotificationManager);
     }
 
     @Test
     public void testCaptivePortalOnPartialConnectivity() throws Exception {
-        final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
-        final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
-        mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+        final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .build();
+        mCm.registerNetworkCallback(wifiRequest, wifiCallback);
 
         final TestNetworkCallback validatedCallback = new TestNetworkCallback();
         final NetworkRequest validatedRequest = new NetworkRequest.Builder()
@@ -4208,21 +4353,28 @@
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         String redirectUrl = "http://android.com/path";
         mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
-        captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl);
 
+        // This is necessary because of b/245893397, the same bug that happens where we use
+        // expectAvailableDoubleValidatedCallbacks.
+        // TODO : fix b/245893397 and remove this.
+        wifiCallback.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, mWiFiNetworkAgent);
+
         // Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
         mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork());
         verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1))
                 .launchCaptivePortalApp();
 
         // Report that the captive portal is dismissed with partial connectivity, and check that
-        // callbacks are fired.
+        // callbacks are fired with PARTIAL and without CAPTIVE_PORTAL.
         mWiFiNetworkAgent.setNetworkPartial();
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
         waitForIdle();
-        captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
-                mWiFiNetworkAgent);
+        wifiCallback.expectCapabilitiesThat(
+                mWiFiNetworkAgent, nc ->
+                nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)
+                && !nc.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
 
         // Report partial connectivity is accepted.
         mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
@@ -4230,13 +4382,12 @@
                 false /* always */);
         waitForIdle();
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
-        captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        wifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-        NetworkCapabilities nc =
-                validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+        validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
                 mWiFiNetworkAgent);
 
-        mCm.unregisterNetworkCallback(captivePortalCallback);
+        mCm.unregisterNetworkCallback(wifiCallback);
         mCm.unregisterNetworkCallback(validatedCallback);
     }
 
@@ -4263,7 +4414,7 @@
         // Take down network.
         // Expect onLost callback.
         mWiFiNetworkAgent.disconnect();
-        captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        captivePortalCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
@@ -4277,7 +4428,7 @@
         // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
-        captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        captivePortalCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
         validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -4286,7 +4437,7 @@
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
         mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
-        validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        validatedCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
     }
 
     @Test
@@ -4318,7 +4469,12 @@
         mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        validatedCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        // This is necessary because of b/245893397, the same bug that happens where we use
+        // expectAvailableDoubleValidatedCallbacks.
+        // TODO : fix b/245893397 and remove this.
+        captivePortalCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                mWiFiNetworkAgent);
 
         // Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
         mCm.startCaptivePortalApp(wifiNetwork);
@@ -4341,7 +4497,7 @@
         mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
         mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-        captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        captivePortalCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         mCm.unregisterNetworkCallback(validatedCallback);
         mCm.unregisterNetworkCallback(captivePortalCallback);
@@ -4605,6 +4761,81 @@
         return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
     }
 
+    // A NetworkSpecifier subclass that matches all networks but must not be visible to apps.
+    static class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
+            Parcelable {
+        public static final Parcelable.Creator<ConfidentialMatchAllNetworkSpecifier> CREATOR =
+                new Parcelable.Creator<ConfidentialMatchAllNetworkSpecifier>() {
+                    public ConfidentialMatchAllNetworkSpecifier createFromParcel(Parcel in) {
+                        return new ConfidentialMatchAllNetworkSpecifier();
+                    }
+
+                    public ConfidentialMatchAllNetworkSpecifier[] newArray(int size) {
+                        return new ConfidentialMatchAllNetworkSpecifier[size];
+                    }
+                };
+        @Override
+        public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+            return true;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {}
+
+        @Override
+        public NetworkSpecifier redact() {
+            return null;
+        }
+    }
+
+    // A network specifier that matches either another LocalNetworkSpecifier with the same
+    // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is.
+    static class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+        public static final Parcelable.Creator<LocalStringNetworkSpecifier> CREATOR =
+                new Parcelable.Creator<LocalStringNetworkSpecifier>() {
+                    public LocalStringNetworkSpecifier createFromParcel(Parcel in) {
+                        return new LocalStringNetworkSpecifier(in);
+                    }
+
+                    public LocalStringNetworkSpecifier[] newArray(int size) {
+                        return new LocalStringNetworkSpecifier[size];
+                    }
+                };
+        private String mString;
+
+        LocalStringNetworkSpecifier(String string) {
+            mString = string;
+        }
+
+        LocalStringNetworkSpecifier(Parcel in) {
+            mString = in.readString();
+        }
+
+        @Override
+        public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+            if (other instanceof LocalStringNetworkSpecifier) {
+                return TextUtils.equals(mString,
+                        ((LocalStringNetworkSpecifier) other).mString);
+            }
+            if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true;
+            return false;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mString);
+        }
+    }
+
     /**
      * Verify request matching behavior with network specifiers.
      *
@@ -4614,56 +4845,6 @@
      */
     @Test
     public void testNetworkSpecifier() throws Exception {
-        // A NetworkSpecifier subclass that matches all networks but must not be visible to apps.
-        class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
-                Parcelable {
-            @Override
-            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
-                return true;
-            }
-
-            @Override
-            public int describeContents() {
-                return 0;
-            }
-
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {}
-
-            @Override
-            public NetworkSpecifier redact() {
-                return null;
-            }
-        }
-
-        // A network specifier that matches either another LocalNetworkSpecifier with the same
-        // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is.
-        class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
-            private String mString;
-
-            LocalStringNetworkSpecifier(String string) {
-                mString = string;
-            }
-
-            @Override
-            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
-                if (other instanceof LocalStringNetworkSpecifier) {
-                    return TextUtils.equals(mString,
-                            ((LocalStringNetworkSpecifier) other).mString);
-                }
-                if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true;
-                return false;
-            }
-
-            @Override
-            public int describeContents() {
-                return 0;
-            }
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {}
-        }
-
-
         NetworkRequest rEmpty1 = newWifiRequestBuilder().build();
         NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build();
         NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build();
@@ -4747,6 +4928,29 @@
         return mContext.getAttributionTag();
     }
 
+    static class NonParcelableSpecifier extends NetworkSpecifier {
+        @Override
+        public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+            return false;
+        }
+    }
+    static class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
+        public static final Parcelable.Creator<NonParcelableSpecifier> CREATOR =
+                new Parcelable.Creator<NonParcelableSpecifier>() {
+                    public NonParcelableSpecifier createFromParcel(Parcel in) {
+                        return new NonParcelableSpecifier();
+                    }
+
+                    public NonParcelableSpecifier[] newArray(int size) {
+                        return new NonParcelableSpecifier[size];
+                    }
+                };
+        @Override public int describeContents() {
+            return 0;
+        }
+        @Override public void writeToParcel(Parcel p, int flags) {}
+    }
+
     @Test
     public void testInvalidNetworkSpecifier() {
         assertThrows(IllegalArgumentException.class, () -> {
@@ -4764,17 +4968,6 @@
                     mContext.getPackageName(), getAttributionTag());
         });
 
-        class NonParcelableSpecifier extends NetworkSpecifier {
-            @Override
-            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
-                return false;
-            }
-        };
-        class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
-            @Override public int describeContents() { return 0; }
-            @Override public void writeToParcel(Parcel p, int flags) {}
-        }
-
         final NetworkRequest.Builder builder =
                 new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
         assertThrows(ClassCastException.class, () -> {
@@ -4841,9 +5034,6 @@
 
     @Test
     public void testRegisterDefaultNetworkCallback() throws Exception {
-        // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
-        mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
-
         final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
         defaultNetworkCallback.assertNoCallback();
@@ -4882,7 +5072,7 @@
 
         // Bring down cell. Expect no default network callback, since it wasn't the default.
         mCellNetworkAgent.disconnect();
-        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         defaultNetworkCallback.assertNoCallback();
         systemDefaultCallback.assertNoCallback();
         assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -4901,14 +5091,14 @@
         // followed by AVAILABLE cell.
         mWiFiNetworkAgent.disconnect();
         cellNetworkCallback.assertNoCallback();
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
-        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         mCellNetworkAgent.disconnect();
-        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
 
@@ -4920,7 +5110,7 @@
         assertEquals(null, systemDefaultCallback.getLastAvailableNetwork());
 
         mMockVpn.disconnect();
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mMockVpn);
         systemDefaultCallback.assertNoCallback();
         waitForIdle();
         assertEquals(null, mCm.getActiveNetwork());
@@ -4949,7 +5139,7 @@
         lp.setInterfaceName("foonet_data0");
         mCellNetworkAgent.sendLinkProperties(lp);
         // We should get onLinkPropertiesChanged().
-        cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
@@ -4957,7 +5147,7 @@
         mCellNetworkAgent.suspend();
         cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
                 mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.SUSPENDED, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState());
 
@@ -4973,7 +5163,7 @@
         mCellNetworkAgent.resume();
         cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
                 mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.RESUMED, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState());
 
@@ -4988,9 +5178,10 @@
     }
 
     @Test
-    public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception {
+    public void testRegisterPrivilegedDefaultCallbacksRequirePermissions() throws Exception {
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(false /* validated */);
+        mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
 
         final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
         final TestNetworkCallback callback = new TestNetworkCallback();
@@ -5001,6 +5192,12 @@
                 () -> mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler));
         callback.assertNoCallback();
 
+        mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_GRANTED);
+        mCm.registerSystemDefaultNetworkCallback(callback, handler);
+        mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
+        callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        mCm.unregisterNetworkCallback(callback);
+
         mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
         mCm.registerSystemDefaultNetworkCallback(callback, handler);
         callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -5044,9 +5241,9 @@
         otherUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         includeOtherUidsCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        otherUidCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        otherUidCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        includeOtherUidsCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // Only the includeOtherUidsCallback sees a VPN that does not apply to its UID.
         final UidRange range = UidRange.createForUser(UserHandle.of(RESTRICTED_USER));
@@ -5057,7 +5254,7 @@
         otherUidCallback.assertNoCallback();
 
         mMockVpn.disconnect();
-        includeOtherUidsCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        includeOtherUidsCallback.expect(CallbackEntry.LOST, mMockVpn);
         callback.assertNoCallback();
         otherUidCallback.assertNoCallback();
     }
@@ -5191,10 +5388,10 @@
 
         // When wifi connects, cell lingers.
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        callback.expectLosing(mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        fgCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        fgCallback.expectLosing(mCellNetworkAgent);
         fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -5202,7 +5399,7 @@
         // When lingering is complete, cell is still there but is now in the background.
         waitForIdle();
         int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
-        fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs);
+        fgCallback.expect(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs);
         // Expect a network capabilities update sans FOREGROUND.
         callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -5225,7 +5422,7 @@
         // Release the request. The network immediately goes into the background, since it was not
         // lingering.
         mCm.unregisterNetworkCallback(cellCallback);
-        fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        fgCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         // Expect a network capabilities update sans FOREGROUND.
         callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -5233,8 +5430,8 @@
 
         // Disconnect wifi and check that cell is foreground again.
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        fgCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        fgCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
 
@@ -5393,7 +5590,7 @@
             // Cell disconnects. There is still the "mobile data always on" request outstanding,
             // and the test factory should see it now that it isn't hopelessly outscored.
             mCellNetworkAgent.disconnect();
-            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
             // Wait for the network to be removed from internal structures before
             // calling synchronous getter
             waitForIdle();
@@ -5416,7 +5613,7 @@
             testFactory.assertRequestCountEquals(0);
             assertFalse(testFactory.getMyStartRequested());
             // ...  and cell data to be torn down immediately since it is no longer nascent.
-            cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
             waitForIdle();
             assertLength(1, mCm.getAllNetworks());
             testFactory.terminate();
@@ -5488,6 +5685,24 @@
     }
 
     @Test
+    public void testActivelyPreferBadWifiSetting() throws Exception {
+        doReturn(1).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
+        mPolicyTracker.reevaluate();
+        waitForIdle();
+        assertTrue(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
+
+        doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
+        mPolicyTracker.reevaluate();
+        waitForIdle();
+        if (SdkLevel.isAtLeastU()) {
+            // U+ ignore the setting and always actively prefers bad wifi
+            assertTrue(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
+        } else {
+            assertFalse(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
+        }
+    }
+
+    @Test
     public void testOffersAvoidsBadWifi() throws Exception {
         // Normal mode : the carrier doesn't restrict moving away from bad wifi.
         // This has getAvoidBadWifi return true.
@@ -5575,7 +5790,7 @@
 
         // Disconnect wifi and pretend the carrier restricts moving away from bad wifi.
         mWiFiNetworkAgent.disconnect();
-        wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        wifiNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         // This has getAvoidBadWifi return false. This test doesn't change the value of the
         // associated setting.
         doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
@@ -5604,6 +5819,52 @@
         wifiCallback.assertNoCallback();
     }
 
+    public void doTestPreferBadWifi(final boolean preferBadWifi) throws Exception {
+        // Pretend we're on a carrier that restricts switching away from bad wifi, and
+        // depending on the parameter one that may indeed prefer bad wifi.
+        doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+        doReturn(preferBadWifi ? 1 : 0).when(mResources)
+                .getInteger(R.integer.config_activelyPreferBadWifi);
+        mPolicyTracker.reevaluate();
+
+        registerDefaultNetworkCallbacks();
+        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .build();
+        final TestableNetworkCallback wifiCallback = new TestableNetworkCallback();
+        mCm.registerNetworkCallback(wifiRequest, wifiCallback);
+
+        // Bring up validated cell and unvalidated wifi.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+        wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        if (preferBadWifi) {
+            expectUnvalidationCheckWillNotify(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
+            mDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        } else {
+            expectUnvalidationCheckWillNotNotify(mWiFiNetworkAgent);
+            mDefaultNetworkCallback.assertNoCallback();
+        }
+    }
+
+    @Test
+    public void testPreferBadWifi_doNotPrefer() throws Exception {
+        // Starting with U this mode is no longer supported and can't actually be tested
+        assumeFalse(SdkLevel.isAtLeastU());
+        doTestPreferBadWifi(false /* preferBadWifi */);
+    }
+
+    @Test
+    public void testPreferBadWifi_doPrefer() throws Exception {
+        doTestPreferBadWifi(true /* preferBadWifi */);
+    }
+
     @Test
     public void testAvoidBadWifi() throws Exception {
         final ContentResolver cr = mServiceContext.getContentResolver();
@@ -5627,7 +5888,8 @@
         TestNetworkCallback validatedWifiCallback = new TestNetworkCallback();
         mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
 
-        Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 0);
+        // Prompt mode, so notifications can be tested
+        Settings.Global.putString(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null);
         mPolicyTracker.reevaluate();
 
         // Bring up validated cell.
@@ -5648,7 +5910,8 @@
         mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        validatedWifiCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        expectNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
 
         // Because avoid bad wifi is off, we don't switch to cellular.
         defaultCallback.assertNoCallback();
@@ -5664,14 +5927,20 @@
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
 
         // Switch back to a restrictive carrier.
         doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
+        // A notification was already shown for this very network.
+        expectNoNotification(mWiFiNetworkAgent);
 
         // Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
+        // In principle this is a little bit unrealistic because the switch to a less restrictive
+        // carrier above should have remove the notification but this doesn't matter for the
+        // purposes of this test.
         mCm.setAvoidUnvalidated(wifiNetwork);
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
@@ -5692,7 +5961,8 @@
         mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(wifiNetwork, false);
         defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        validatedWifiCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        expectNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
 
         // Simulate the user selecting "switch" and checking the don't ask again checkbox.
         Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1);
@@ -5705,6 +5975,7 @@
         assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
                 NET_CAPABILITY_VALIDATED));
         assertEquals(mCm.getActiveNetwork(), cellNetwork);
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
 
         // Simulate the user turning the cellular fallback setting off and then on.
         // We switch to wifi and then to cell.
@@ -5712,6 +5983,9 @@
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         assertEquals(mCm.getActiveNetwork(), wifiNetwork);
+        // Notification is cleared again because CS doesn't particularly remember that it has
+        // cleared it before, and if it hasn't cleared it before then it should do so now.
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
         Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1);
         mPolicyTracker.reevaluate();
         defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
@@ -5719,9 +5993,11 @@
 
         // If cell goes down, we switch to wifi.
         mCellNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
         validatedWifiCallback.assertNoCallback();
+        // Notification is cleared yet again because the device switched to wifi.
+        expectClearNotification(mWiFiNetworkAgent, NotificationType.LOST_INTERNET);
 
         mCm.unregisterNetworkCallback(cellNetworkCallback);
         mCm.unregisterNetworkCallback(validatedWifiCallback);
@@ -5783,7 +6059,7 @@
         networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
         mWiFiNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         // Validate that UNAVAILABLE is not called
         networkCallback.assertNoCallback();
@@ -5803,7 +6079,7 @@
         mCm.requestNetwork(nr, networkCallback, timeoutMs);
 
         // pass timeout and validate that UNAVAILABLE is called
-        networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
+        networkCallback.expect(CallbackEntry.UNAVAILABLE);
 
         // create a network satisfying request - validate that request not triggered
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -5887,7 +6163,7 @@
                 // onUnavailable!
                 testFactory.triggerUnfulfillable(newRequest);
 
-                networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
+                networkCallback.expect(CallbackEntry.UNAVAILABLE);
 
                 // Declaring a request unfulfillable releases it automatically.
                 testFactory.expectRequestRemove();
@@ -5969,20 +6245,20 @@
             mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
         }
 
-        private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+        private void expect(CallbackValue callbackValue) throws InterruptedException {
             assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         }
 
         public void expectStarted() throws Exception {
-            expectCallback(new CallbackValue(CallbackType.ON_STARTED));
+            expect(new CallbackValue(CallbackType.ON_STARTED));
         }
 
         public void expectStopped() throws Exception {
-            expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
+            expect(new CallbackValue(CallbackType.ON_STOPPED));
         }
 
         public void expectError(int error) throws Exception {
-            expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
+            expect(new CallbackValue(CallbackType.ON_ERROR, error));
         }
     }
 
@@ -6042,21 +6318,21 @@
             mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
         }
 
-        private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+        private void expect(CallbackValue callbackValue) throws InterruptedException {
             assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
         }
 
         public void expectStarted() throws InterruptedException {
-            expectCallback(new CallbackValue(CallbackType.ON_STARTED));
+            expect(new CallbackValue(CallbackType.ON_STARTED));
         }
 
         public void expectStopped() throws InterruptedException {
-            expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
+            expect(new CallbackValue(CallbackType.ON_STOPPED));
         }
 
         public void expectError(int error) throws InterruptedException {
-            expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
+            expect(new CallbackValue(CallbackType.ON_ERROR, error));
         }
 
         public void assertNoCallback() {
@@ -6852,7 +7128,7 @@
 
         // Disconnect wifi aware network.
         wifiAware.disconnect();
-        callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackEntry.Lost);
+        callback.expect(CallbackEntry.LOST, TIMEOUT_MS);
         mCm.unregisterNetworkCallback(callback);
 
         verifyNoNetwork();
@@ -6899,12 +7175,12 @@
         // ConnectivityService.
         TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
         networkAgent.connect(true);
-        networkCallback.expectCallback(CallbackEntry.AVAILABLE, networkAgent);
-        networkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent);
+        networkCallback.expect(CallbackEntry.AVAILABLE, networkAgent);
+        networkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent);
         CallbackEntry.LinkPropertiesChanged cbi =
-                networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+                networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 networkAgent);
-        networkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, networkAgent);
+        networkCallback.expect(CallbackEntry.BLOCKED_STATUS, networkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
         networkCallback.assertNoCallback();
         checkDirectlyConnectedRoutes(cbi.getLp(), asList(myIpv4Address),
@@ -6919,7 +7195,7 @@
         newLp.addLinkAddress(myIpv6Address1);
         newLp.addLinkAddress(myIpv6Address2);
         networkAgent.sendLinkProperties(newLp);
-        cbi = networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent);
+        cbi = networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent);
         networkCallback.assertNoCallback();
         checkDirectlyConnectedRoutes(cbi.getLp(),
                 asList(myIpv4Address, myIpv6Address1, myIpv6Address2),
@@ -6927,17 +7203,38 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
-    private void expectNotifyNetworkStatus(List<Network> defaultNetworks, String defaultIface,
-            Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception {
+    private void expectNotifyNetworkStatus(List<Network> allNetworks, List<Network> defaultNetworks,
+            String defaultIface, Integer vpnUid, String vpnIfname, List<String> underlyingIfaces)
+            throws Exception {
         ArgumentCaptor<List<Network>> defaultNetworksCaptor = ArgumentCaptor.forClass(List.class);
         ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
                 ArgumentCaptor.forClass(List.class);
+        ArgumentCaptor<List<NetworkStateSnapshot>> snapshotsCaptor =
+                ArgumentCaptor.forClass(List.class);
 
         verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(defaultNetworksCaptor.capture(),
-                any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
+                snapshotsCaptor.capture(), eq(defaultIface), vpnInfosCaptor.capture());
 
         assertSameElements(defaultNetworks, defaultNetworksCaptor.getValue());
 
+        List<Network> snapshotNetworks = new ArrayList<Network>();
+        for (NetworkStateSnapshot ns : snapshotsCaptor.getValue()) {
+            snapshotNetworks.add(ns.getNetwork());
+        }
+        assertSameElements(allNetworks, snapshotNetworks);
+
+        if (defaultIface != null) {
+            assertNotNull(
+                    "Did not find interface " + defaultIface + " in call to notifyNetworkStatus",
+                    CollectionUtils.findFirst(snapshotsCaptor.getValue(), (ns) -> {
+                        final LinkProperties lp = ns.getLinkProperties();
+                        if (lp != null && TextUtils.equals(defaultIface, lp.getInterfaceName())) {
+                            return true;
+                        }
+                        return false;
+                    }));
+        }
+
         List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
         if (vpnUid != null) {
             assertEquals("Should have exactly one VPN:", 1, infos.size());
@@ -6952,28 +7249,47 @@
     }
 
     private void expectNotifyNetworkStatus(
-            List<Network> defaultNetworks, String defaultIface) throws Exception {
-        expectNotifyNetworkStatus(defaultNetworks, defaultIface, null, null, List.of());
+            List<Network> allNetworks, List<Network> defaultNetworks, String defaultIface)
+            throws Exception {
+        expectNotifyNetworkStatus(allNetworks, defaultNetworks, defaultIface, null, null,
+                List.of());
+    }
+
+    private List<Network> onlyCell() {
+        return List.of(mCellNetworkAgent.getNetwork());
+    }
+
+    private List<Network> onlyWifi() {
+        return List.of(mWiFiNetworkAgent.getNetwork());
+    }
+
+    private List<Network> cellAndWifi() {
+        return List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork());
     }
 
     @Test
     public void testStatsIfacesChanged() throws Exception {
-        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-
-        final List<Network> onlyCell = List.of(mCellNetworkAgent.getNetwork());
-        final List<Network> onlyWifi = List.of(mWiFiNetworkAgent.getNetwork());
-
         LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
         LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
 
-        // Simple connection should have updated ifaces
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+        // Simple connection with initial LP should have updated ifaces.
         mCellNetworkAgent.connect(false);
-        mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
+        reset(mStatsManager);
+
+        // Verify change fields other than interfaces does not trigger a notification to NSS.
+        cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+                MOBILE_IFNAME));
+        cellLp.setDnsServers(List.of(InetAddress.getAllByName("8.8.8.8")));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        verifyNoMoreInteractions(mStatsManager);
         reset(mStatsManager);
 
         // Default network switch should update ifaces.
@@ -6981,37 +7297,63 @@
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectNotifyNetworkStatus(onlyWifi, WIFI_IFNAME);
+        expectNotifyNetworkStatus(cellAndWifi(), onlyWifi(), WIFI_IFNAME);
+        reset(mStatsManager);
+
+        // Disconnecting a network updates ifaces again. The soon-to-be disconnected interface is
+        // still in the list to ensure that stats are counted on that interface.
+        // TODO: this means that if anything else uses that interface for any other reason before
+        // notifyNetworkStatus is called again, traffic on that interface will be accounted to the
+        // disconnected network. This is likely a bug in ConnectivityService; it should probably
+        // call notifyNetworkStatus again without the disconnected network.
+        mCellNetworkAgent.disconnect();
+        waitForIdle();
+        expectNotifyNetworkStatus(cellAndWifi(), onlyWifi(), WIFI_IFNAME);
+        verifyNoMoreInteractions(mStatsManager);
+        reset(mStatsManager);
+
+        // Connecting a network updates ifaces even if the network doesn't become default.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+        mCellNetworkAgent.connect(false);
+        waitForIdle();
+        expectNotifyNetworkStatus(cellAndWifi(), onlyWifi(), WIFI_IFNAME);
         reset(mStatsManager);
 
         // Disconnect should update ifaces.
         mWiFiNetworkAgent.disconnect();
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Metered change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
         reset(mStatsManager);
 
         mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Temp metered change shouldn't update ifaces
         mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
         waitForIdle();
-        verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell),
+        verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell()),
+                any(List.class), eq(MOBILE_IFNAME), any(List.class));
+        reset(mStatsManager);
+
+        // Congested change shouldn't update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
+        waitForIdle();
+        verify(mStatsManager, never()).notifyNetworkStatus(eq(onlyCell()),
                 any(List.class), eq(MOBILE_IFNAME), any(List.class));
         reset(mStatsManager);
 
         // Roaming change should update ifaces
         mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell, MOBILE_IFNAME);
+        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Test VPNs.
@@ -7025,8 +7367,8 @@
                 List.of(mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork());
 
         // A VPN with default (null) underlying networks sets the underlying network's interfaces...
-        expectNotifyNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(MOBILE_IFNAME));
+        expectNotifyNetworkStatus(cellAndVpn, cellAndVpn, MOBILE_IFNAME, Process.myUid(),
+                VPN_IFNAME, List.of(MOBILE_IFNAME));
 
         // ...and updates them as the default network switches.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -7035,15 +7377,16 @@
         final Network[] onlyNull = new Network[]{null};
         final List<Network> wifiAndVpn =
                 List.of(mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork());
-        final List<Network> cellAndWifi =
-                List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork());
+        final List<Network> cellWifiAndVpn =
+                List.of(mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork(),
+                        mMockVpn.getNetwork());
         final Network[] cellNullAndWifi =
                 new Network[]{mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
 
         waitForIdle();
         assertEquals(wifiLp, mService.getActiveLinkProperties());
-        expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(WIFI_IFNAME));
+        expectNotifyNetworkStatus(cellWifiAndVpn, wifiAndVpn, WIFI_IFNAME, Process.myUid(),
+                VPN_IFNAME, List.of(WIFI_IFNAME));
         reset(mStatsManager);
 
         // A VPN that sets its underlying networks passes the underlying interfaces, and influences
@@ -7052,23 +7395,23 @@
         // MOBILE_IFNAME even though the default network is wifi.
         // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
         // applies to the system server UID should not have any bearing on network stats.
-        mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
+        mMockVpn.setUnderlyingNetworks(onlyCell().toArray(new Network[0]));
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(MOBILE_IFNAME));
+        expectNotifyNetworkStatus(cellWifiAndVpn, wifiAndVpn, MOBILE_IFNAME, Process.myUid(),
+                VPN_IFNAME, List.of(MOBILE_IFNAME));
         reset(mStatsManager);
 
-        mMockVpn.setUnderlyingNetworks(cellAndWifi.toArray(new Network[0]));
+        mMockVpn.setUnderlyingNetworks(cellAndWifi().toArray(new Network[0]));
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(MOBILE_IFNAME, WIFI_IFNAME));
+        expectNotifyNetworkStatus(cellWifiAndVpn, wifiAndVpn, MOBILE_IFNAME, Process.myUid(),
+                VPN_IFNAME,  List.of(MOBILE_IFNAME, WIFI_IFNAME));
         reset(mStatsManager);
 
         // Null underlying networks are ignored.
         mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(MOBILE_IFNAME, WIFI_IFNAME));
+        expectNotifyNetworkStatus(cellWifiAndVpn, wifiAndVpn, MOBILE_IFNAME, Process.myUid(),
+                VPN_IFNAME,  List.of(MOBILE_IFNAME, WIFI_IFNAME));
         reset(mStatsManager);
 
         // If an underlying network disconnects, that interface should no longer be underlying.
@@ -7081,8 +7424,8 @@
         mCellNetworkAgent.disconnect();
         waitForIdle();
         assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
-        expectNotifyNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
-                List.of(MOBILE_IFNAME, WIFI_IFNAME));
+        expectNotifyNetworkStatus(cellWifiAndVpn, wifiAndVpn, MOBILE_IFNAME, Process.myUid(),
+                VPN_IFNAME, List.of(MOBILE_IFNAME, WIFI_IFNAME));
 
         // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
         // network for the VPN...
@@ -7117,25 +7460,25 @@
         // Also, for the same reason as above, the active interface passed in is null.
         mMockVpn.setUnderlyingNetworks(new Network[0]);
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, wifiAndVpn, null);
         reset(mStatsManager);
 
         // Specifying only a null underlying network is the same as no networks.
         mMockVpn.setUnderlyingNetworks(onlyNull);
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, wifiAndVpn, null);
         reset(mStatsManager);
 
         // Specifying networks that are all disconnected is the same as specifying no networks.
-        mMockVpn.setUnderlyingNetworks(onlyCell.toArray(new Network[0]));
+        mMockVpn.setUnderlyingNetworks(onlyCell().toArray(new Network[0]));
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, null);
+        expectNotifyNetworkStatus(wifiAndVpn, wifiAndVpn, null);
         reset(mStatsManager);
 
         // Passing in null again means follow the default network again.
         mMockVpn.setUnderlyingNetworks(null);
         waitForIdle();
-        expectNotifyNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+        expectNotifyNetworkStatus(wifiAndVpn, wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
                 List.of(WIFI_IFNAME));
         reset(mStatsManager);
     }
@@ -7154,7 +7497,7 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
         TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(callback);
-        callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        callback.expect(CallbackEntry.AVAILABLE, mCellNetworkAgent);
         callback.expectCapabilitiesThat(
                 mCellNetworkAgent, nc -> Arrays.equals(adminUids, nc.getAdministratorUids()));
         mCm.unregisterNetworkCallback(callback);
@@ -7165,7 +7508,7 @@
         mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
         callback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(callback);
-        callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        callback.expect(CallbackEntry.AVAILABLE, mCellNetworkAgent);
         callback.expectCapabilitiesThat(
                 mCellNetworkAgent, nc -> nc.getAdministratorUids().length == 0);
     }
@@ -7190,7 +7533,7 @@
         mWiFiNetworkAgent.connect(true /* validated */);
 
         final List<Network> none = List.of();
-        expectNotifyNetworkStatus(none, null);  // Wifi is not the default network
+        expectNotifyNetworkStatus(onlyWifi(), none, null);  // Wifi is not the default network
 
         // Create a virtual network based on the wifi network.
         final int ownerUid = 10042;
@@ -7215,7 +7558,9 @@
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
         final List<Network> onlyVcn = List.of(vcn.getNetwork());
-        expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(WIFI_IFNAME));
+        final List<Network> vcnAndWifi = List.of(vcn.getNetwork(), mWiFiNetworkAgent.getNetwork());
+        expectNotifyNetworkStatus(vcnAndWifi, onlyVcn, vcnIface, ownerUid, vcnIface,
+                List.of(WIFI_IFNAME));
 
         // Add NOT_METERED to the underlying network, check that it is not propagated.
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -7238,7 +7583,10 @@
         nc = mCm.getNetworkCapabilities(vcn.getNetwork());
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_ROAMING));
-        expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(MOBILE_IFNAME));
+        final List<Network> vcnWifiAndCell = List.of(vcn.getNetwork(),
+                mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork());
+        expectNotifyNetworkStatus(vcnWifiAndCell, onlyVcn, vcnIface, ownerUid, vcnIface,
+                List.of(MOBILE_IFNAME));
     }
 
     @Test
@@ -7419,12 +7767,12 @@
         assertTrue(new ArraySet<>(resolvrParams.tlsServers).containsAll(
                 asList("2001:db8::1", "192.0.2.1")));
         reset(mMockDnsResolver);
-        cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
+        cellNetworkCallback.expect(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
                 mCellNetworkAgent);
-        CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+        CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expect(
                 CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(cbi.getLp().isPrivateDnsActive());
         assertNull(cbi.getLp().getPrivateDnsServerName());
@@ -7455,7 +7803,7 @@
         setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
         // Can't test dns configuration for strict mode without properly mocking
         // out the DNS lookups, but can test that LinkProperties is updated.
-        cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cbi = cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -7488,12 +7836,12 @@
         mCellNetworkAgent.sendLinkProperties(lp);
         mCellNetworkAgent.connect(false);
         waitForIdle();
-        cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
+        cellNetworkCallback.expect(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
                 mCellNetworkAgent);
-        CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+        CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expect(
                 CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
-        cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(cbi.getLp().isPrivateDnsActive());
         assertNull(cbi.getLp().getPrivateDnsServerName());
@@ -7511,7 +7859,7 @@
         LinkProperties lp2 = new LinkProperties(lp);
         lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
         mCellNetworkAgent.sendLinkProperties(lp2);
-        cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cbi = cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(cbi.getLp().isPrivateDnsActive());
@@ -7538,7 +7886,7 @@
         mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent(
                 makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId,
                         "145.100.185.16", "", VALIDATION_RESULT_SUCCESS));
-        cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cbi = cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -7550,7 +7898,7 @@
         LinkProperties lp3 = new LinkProperties(lp2);
         lp3.setMtu(1300);
         mCellNetworkAgent.sendLinkProperties(lp3);
-        cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cbi = cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -7563,7 +7911,7 @@
         LinkProperties lp4 = new LinkProperties(lp3);
         lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
         mCellNetworkAgent.sendLinkProperties(lp4);
-        cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        cbi = cellNetworkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(cbi.getLp().isPrivateDnsActive());
@@ -7755,7 +8103,7 @@
             mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
             // onCapabilitiesChanged() should be called because
             // NetworkCapabilities#mUnderlyingNetworks is updated.
-            CallbackEntry ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
+            CallbackEntry ce = callback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
                     mMockVpn);
             final NetworkCapabilities vpnNc1 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
             // Since the wifi network hasn't brought up,
@@ -7792,7 +8140,7 @@
             // 2. When a network connects, updateNetworkInfo propagates underlying network
             //    capabilities before rematching networks.
             // Given that this scenario can't really happen, this is probably fine for now.
-            ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+            ce = callback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
             final NetworkCapabilities vpnNc2 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
             // The wifi network is brought up, NetworkCapabilities#mUnderlyingNetworks is updated to
             // it.
@@ -7806,7 +8154,7 @@
 
             // Disconnect the network, and expect to see the VPN capabilities change accordingly.
             mWiFiNetworkAgent.disconnect();
-            callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+            callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
             callback.expectCapabilitiesThat(mMockVpn, (nc) ->
                     nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
 
@@ -7852,7 +8200,7 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_CELLULAR));
-        callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+        callback.expect(CallbackEntry.SUSPENDED, mMockVpn);
         callback.assertNoCallback();
 
         assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
@@ -7870,7 +8218,7 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_WIFI));
-        callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        callback.expect(CallbackEntry.RESUMED, mMockVpn);
         callback.assertNoCallback();
 
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
@@ -7907,7 +8255,7 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_CELLULAR));
-        callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+        callback.expect(CallbackEntry.SUSPENDED, mMockVpn);
         callback.assertNoCallback();
 
         assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
@@ -7923,7 +8271,7 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_CELLULAR));
-        callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        callback.expect(CallbackEntry.RESUMED, mMockVpn);
         callback.assertNoCallback();
 
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
@@ -7937,9 +8285,6 @@
 
     @Test
     public void testVpnNetworkActive() throws Exception {
-        // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
-        mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
-
         final int uid = Process.myUid();
 
         final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -7983,6 +8328,7 @@
         // VPN networks do not satisfy the default request and are automatically validated
         // by NetworkMonitor
         assertFalse(NetworkMonitorUtils.isValidationRequired(
+                false /* isDunValidationRequired */,
                 NetworkAgentConfigShimImpl.newInstance(mMockVpn.getNetworkAgentConfig())
                         .isVpnValidationRequired(),
                 mMockVpn.getAgent().getNetworkCapabilities()));
@@ -8003,10 +8349,10 @@
         ranges.clear();
         mMockVpn.setUids(ranges);
 
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.LOST, mMockVpn);
 
         // TODO : The default network callback should actually get a LOST call here (also see the
         // comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -8014,7 +8360,7 @@
         // can't currently update their UIDs without disconnecting, so this does not matter too
         // much, but that is the reason the test here has to check for an update to the
         // capabilities instead of the expected LOST then AVAILABLE.
-        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+        defaultCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
         systemDefaultCallback.assertNoCallback();
 
         ranges.add(new UidRange(uid, uid));
@@ -8026,25 +8372,25 @@
         vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
         // TODO : Here like above, AVAILABLE would be correct, but because this can't actually
         // happen outside of the test, ConnectivityService does not rematch callbacks.
-        defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+        defaultCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
         systemDefaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent.disconnect();
 
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        genericNotVpnNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        genericNotVpnNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        wifiNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         vpnNetworkCallback.assertNoCallback();
         defaultCallback.assertNoCallback();
-        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
 
         mMockVpn.disconnect();
 
-        genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        genericNetworkCallback.expect(CallbackEntry.LOST, mMockVpn);
         genericNotVpnNetworkCallback.assertNoCallback();
         wifiNetworkCallback.assertNoCallback();
-        vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expect(CallbackEntry.LOST, mMockVpn);
         systemDefaultCallback.assertNoCallback();
         assertEquals(null, mCm.getActiveNetwork());
 
@@ -8102,7 +8448,7 @@
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
         mMockVpn.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expect(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
         mCm.unregisterNetworkCallback(defaultCallback);
@@ -8135,6 +8481,7 @@
         assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
 
         assertFalse(NetworkMonitorUtils.isValidationRequired(
+                false /* isDunValidationRequired */,
                 NetworkAgentConfigShimImpl.newInstance(mMockVpn.getNetworkAgentConfig())
                         .isVpnValidationRequired(),
                 mMockVpn.getAgent().getNetworkCapabilities()));
@@ -8150,7 +8497,7 @@
         callback.assertNoCallback();
 
         mMockVpn.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        callback.expect(CallbackEntry.LOST, mMockVpn);
         callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
     }
 
@@ -8284,7 +8631,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.SUSPENDED, mMockVpn);
 
         // Add NOT_SUSPENDED again and observe VPN is no longer suspended.
         mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -8293,7 +8640,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.RESUMED, mMockVpn);
 
         // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
         mMockVpn.setUnderlyingNetworks(
@@ -8329,7 +8676,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.SUSPENDED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
@@ -8341,7 +8688,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        vpnNetworkCallback.expect(CallbackEntry.RESUMED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Disconnect cell. Receive update without even removing the dead network from the
@@ -8482,7 +8829,7 @@
 
         // Change the VPN's capabilities somehow (specifically, disconnect wifi).
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         callback.expectCapabilitiesThat(mMockVpn, (caps)
                 -> caps.getUids().size() == 2
                 && caps.getUids().contains(singleUidRange)
@@ -8906,7 +9253,7 @@
 
         // Switch to METERED network. Restrict the use of the network.
         mWiFiNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
 
         // Network becomes NOT_METERED.
@@ -8920,7 +9267,7 @@
         defaultCallback.assertNoCallback();
 
         mCellNetworkAgent.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         defaultCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(defaultCallback);
@@ -8984,10 +9331,9 @@
 
         // Expect exactly one blocked callback for each agent.
         for (int i = 0; i < agents.length; i++) {
-            CallbackEntry e = callback.expectCallbackThat(TIMEOUT_MS, (c) ->
-                    c instanceof CallbackEntry.BlockedStatus
-                            && ((CallbackEntry.BlockedStatus) c).getBlocked() == blocked);
-            Network network = e.getNetwork();
+            final CallbackEntry e = callback.expect(CallbackEntry.BLOCKED_STATUS, TIMEOUT_MS,
+                    c -> c.getBlocked() == blocked);
+            final Network network = e.getNetwork();
             assertTrue("Received unexpected blocked callback for network " + network,
                     expectedNetworks.remove(network));
         }
@@ -9192,7 +9538,7 @@
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
 
         mMockVpn.disconnect();
-        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expect(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
         vpnUidCallback.assertNoCallback();
         vpnUidDefaultCallback.assertNoCallback();
@@ -9283,8 +9629,6 @@
     public void testLegacyLockdownVpn() throws Exception {
         mServiceContext.setPermission(
                 Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
-        // For LockdownVpnTracker to call registerSystemDefaultNetworkCallback.
-        mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
 
         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         final TestNetworkCallback callback = new TestNetworkCallback();
@@ -9344,9 +9688,9 @@
         cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25"));
         cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
-        systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+        callback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED,
                 mCellNetworkAgent);
         waitForIdle();
         assertNull(mMockVpn.getAgent());
@@ -9355,9 +9699,9 @@
         // Expect lockdown VPN to come up.
         ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
         mCellNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         b1.expectBroadcast();
 
         // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
@@ -9435,8 +9779,8 @@
         // fact that a VPN is connected should only result in the VPN itself being unblocked, not
         // any other network. Bug in isUidBlockedByVpn?
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        callback.expectCallback(CallbackEntry.LOST, mMockVpn);
-        defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        callback.expect(CallbackEntry.LOST, mMockVpn);
+        defaultCallback.expect(CallbackEntry.LOST, mMockVpn);
         defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
         systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
 
@@ -9469,7 +9813,7 @@
 
         // Disconnect cell. Nothing much happens since it's not the default network.
         mCellNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         defaultCallback.assertNoCallback();
         systemDefaultCallback.assertNoCallback();
 
@@ -9482,12 +9826,12 @@
         b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
         b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        systemDefaultCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         b1.expectBroadcast();
         callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
         mMockVpn.expectStopVpnRunnerPrivileged();
-        callback.expectCallback(CallbackEntry.LOST, mMockVpn);
+        callback.expect(CallbackEntry.LOST, mMockVpn);
         b2.expectBroadcast();
 
         VMSHandlerThread.quitSafely();
@@ -9659,7 +10003,7 @@
             reset(mMockNetd);
 
             mCellNetworkAgent.removeCapability(testCap);
-            callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            callbackWithCap.expect(CallbackEntry.LOST, mCellNetworkAgent);
             callbackWithoutCap.assertNoCallback();
             verify(mMockNetd).networkClearDefault();
 
@@ -9849,7 +10193,7 @@
         // the NAT64 prefix was removed because one was never discovered.
         cellLp.addLinkAddress(myIpv4);
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         assertRoutesAdded(cellNetId, ipv4Subnet);
         verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
         verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
@@ -9872,7 +10216,7 @@
         // Remove IPv4 address. Expect prefix discovery to be started again.
         cellLp.removeLinkAddress(myIpv4);
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
         assertRoutesRemoved(cellNetId, ipv4Subnet);
 
@@ -9881,7 +10225,7 @@
         assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
                 makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
-        LinkProperties lpBeforeClat = networkCallback.expectCallback(
+        LinkProperties lpBeforeClat = networkCallback.expect(
                 CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
         assertEquals(0, lpBeforeClat.getStackedLinks().size());
         assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
@@ -9889,7 +10233,7 @@
 
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
                 .getStackedLinks();
         assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
@@ -9898,7 +10242,7 @@
         // Change trivial linkproperties and see if stacked link is preserved.
         cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
 
         List<LinkProperties> stackedLpsAfterChange =
                 mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
@@ -9947,13 +10291,13 @@
         cellLp.addLinkAddress(myIpv4);
         cellLp.addRoute(ipv4Subnet);
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         assertRoutesAdded(cellNetId, ipv4Subnet);
         verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
 
         // As soon as stop is called, the linkproperties lose the stacked interface.
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
         LinkProperties expected = new LinkProperties(cellLp);
         expected.setNat64Prefix(kOtherNat64Prefix);
@@ -9985,12 +10329,12 @@
         cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         assertRoutesRemoved(cellNetId, ipv4Subnet);  // Directly-connected routes auto-added.
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
         mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
                 cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
 
         // Clat iface comes up. Expect stacked link to be added.
@@ -10015,7 +10359,7 @@
         verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_MOBILE_IFNAME);
         // Clean up.
         mCellNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         networkCallback.assertNoCallback();
         verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
                 eq(Integer.toString(TRANSPORT_CELLULAR)));
@@ -10054,7 +10398,7 @@
 
         // Disconnect the network. clat is stopped and the network is destroyed.
         mCellNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         networkCallback.assertNoCallback();
         verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
         verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
@@ -10228,7 +10572,7 @@
         // clat has been stopped, or the test will be flaky.
         ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
         mWiFiNetworkAgent.disconnect();
-        callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        callback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         b.expectBroadcast();
 
         verifyClatdStop(inOrder, iface);
@@ -10302,7 +10646,7 @@
         // Network switch
         mWiFiNetworkAgent.connect(true);
         networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        networkCallback.expectLosing(mCellNetworkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
                 eq(Integer.toString(TRANSPORT_WIFI)));
@@ -10312,7 +10656,7 @@
         // Disconnect wifi and switch back to cell
         reset(mMockNetd);
         mWiFiNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         assertNoCallbacks(networkCallback);
         verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(),
                 eq(Integer.toString(TRANSPORT_WIFI)));
@@ -10326,7 +10670,7 @@
         mWiFiNetworkAgent.sendLinkProperties(wifiLp);
         mWiFiNetworkAgent.connect(true);
         networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        networkCallback.expectLosing(mCellNetworkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
                 eq(Integer.toString(TRANSPORT_WIFI)));
@@ -10336,7 +10680,7 @@
         // Disconnect cell
         reset(mMockNetd);
         mCellNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
         // sent as network being switched. Ensure rule removal for cell will not be triggered
         // unexpectedly before network being removed.
@@ -10386,11 +10730,11 @@
         LinkProperties lp = new LinkProperties();
         lp.setTcpBufferSizes(testTcpBufferSizes);
         mCellNetworkAgent.sendLinkProperties(lp);
-        networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verifyTcpBufferSizeChange(testTcpBufferSizes);
         // Clean up.
         mCellNetworkAgent.disconnect();
-        networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        networkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         networkCallback.assertNoCallback();
         mCm.unregisterNetworkCallback(networkCallback);
     }
@@ -12008,9 +12352,9 @@
         // While the default callback doesn't see the network before it's validated, the listen
         // sees the network come up and validate later
         allNetworksCb.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
-        allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+        allNetworksCb.expectLosing(mCellNetworkAgent);
         allNetworksCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
-        allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+        allNetworksCb.expect(CallbackEntry.LOST, mCellNetworkAgent,
                 TEST_LINGER_DELAY_MS * 2);
 
         // The cell network has disconnected (see LOST above) because it was outscored and
@@ -12022,7 +12366,7 @@
 
         // The cell network gets torn down right away.
         allNetworksCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
-        allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+        allNetworksCb.expect(CallbackEntry.LOST, mCellNetworkAgent,
                 TEST_NASCENT_DELAY_MS * 2);
         allNetworksCb.assertNoCallback();
 
@@ -12039,8 +12383,8 @@
 
         mWiFiNetworkAgent.disconnect();
 
-        allNetworksCb.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        allNetworksCb.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
 
         // Reconnect a WiFi network and make sure the cell network is still not torn down.
@@ -12053,10 +12397,8 @@
         // Now remove the reason to keep connected and make sure the network lingers and is
         // torn down.
         mCellNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).build());
-        allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent,
-                TEST_NASCENT_DELAY_MS * 2);
-        allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
-                TEST_LINGER_DELAY_MS * 2);
+        allNetworksCb.expectLosing(mCellNetworkAgent, TEST_NASCENT_DELAY_MS * 2);
+        allNetworksCb.expect(CallbackEntry.LOST, mCellNetworkAgent, TEST_LINGER_DELAY_MS * 2);
         mDefaultNetworkCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(allNetworksCb);
@@ -12784,8 +13126,6 @@
             throw new IllegalStateException("Default network callbacks already registered");
         }
 
-        // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback()
-        mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
         mSystemDefaultNetworkCallback = new TestNetworkCallback();
         mDefaultNetworkCallback = new TestNetworkCallback();
         mProfileDefaultNetworkCallback = new TestNetworkCallback();
@@ -12825,6 +13165,12 @@
         if (null != mTestPackageDefaultNetworkCallback2) {
             mCm.unregisterNetworkCallback(mTestPackageDefaultNetworkCallback2);
         }
+        mSystemDefaultNetworkCallback = null;
+        mDefaultNetworkCallback = null;
+        mProfileDefaultNetworkCallback = null;
+        mTestPackageDefaultNetworkCallback = null;
+        mProfileDefaultNetworkCallbackAsAppUid2 = null;
+        mTestPackageDefaultNetworkCallback2 = null;
     }
 
     private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -13021,7 +13367,7 @@
         setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
 
         // At this point, with no network is available, the lost callback should trigger
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
         otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
@@ -13067,7 +13413,7 @@
         otherUidDefaultCallback.assertNoCallback();
 
         // At this point, with no network is available, the lost callback should trigger
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
         otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
@@ -13129,13 +13475,13 @@
         // Since the callback didn't use the per-app network, only the other UID gets a callback.
         // Because the preference specifies no fallback, it does not switch to cellular.
         defaultNetworkCallback.assertNoCallback();
-        otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+        otherUidDefaultCallback.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
 
         // Now bring down the default network.
         setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
 
         // As this callback was tracking the default, this should now trigger.
-        defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        defaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         otherUidDefaultCallback.assertNoCallback();
 
         // Confirm we can unregister without issues.
@@ -14249,7 +14595,7 @@
         mWiFiNetworkAgent.disconnect();
         bestMatchingCb.assertNoCallback();
         mCellNetworkAgent.disconnect();
-        bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        bestMatchingCb.expect(CallbackEntry.LOST, mCellNetworkAgent);
     }
 
     private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
@@ -14394,7 +14740,7 @@
         if (allowFallback && !connectWorkProfileAgentAhead) {
             assertNoCallbacks(profileDefaultNetworkCallback);
         } else if (!connectWorkProfileAgentAhead) {
-            profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            profileDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
             if (disAllowProfileDefaultNetworkCallback != null) {
                 assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
             }
@@ -14464,10 +14810,10 @@
         // apps on this network see the appropriate callbacks, and the app on the work profile
         // doesn't because it continues to use the enterprise network.
         mCellNetworkAgent.disconnect();
-        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mSystemDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         if (disAllowProfileDefaultNetworkCallback != null) {
-            disAllowProfileDefaultNetworkCallback.expectCallback(
+            disAllowProfileDefaultNetworkCallback.expect(
                     CallbackEntry.LOST, mCellNetworkAgent);
         }
         profileDefaultNetworkCallback.assertNoCallback();
@@ -14489,7 +14835,7 @@
         // When the agent disconnects, test that the app on the work profile falls back to the
         // default network.
         workAgent.disconnect();
-        profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
+        profileDefaultNetworkCallback.expect(CallbackEntry.LOST, workAgent);
         if (allowFallback) {
             profileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
             if (disAllowProfileDefaultNetworkCallback != null) {
@@ -14506,14 +14852,14 @@
         inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
 
         mCellNetworkAgent.disconnect();
-        mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        mSystemDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         if (disAllowProfileDefaultNetworkCallback != null) {
-            disAllowProfileDefaultNetworkCallback.expectCallback(
+            disAllowProfileDefaultNetworkCallback.expect(
                     CallbackEntry.LOST, mCellNetworkAgent);
         }
         if (allowFallback) {
-            profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            profileDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         }
 
         // Waiting for the handler to be idle before checking for networkDestroy is necessary
@@ -14556,7 +14902,7 @@
         // When the agent disconnects, test that the app on the work profile fall back to the
         // default network.
         workAgent2.disconnect();
-        profileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
+        profileDefaultNetworkCallback.expect(CallbackEntry.LOST, workAgent2);
         if (disAllowProfileDefaultNetworkCallback != null) {
             assertNoCallbacks(disAllowProfileDefaultNetworkCallback);
         }
@@ -15166,11 +15512,11 @@
         workAgent4.disconnect();
         workAgent5.disconnect();
 
-        appCb1.expectCallback(CallbackEntry.LOST, workAgent1);
-        appCb2.expectCallback(CallbackEntry.LOST, workAgent2);
-        appCb3.expectCallback(CallbackEntry.LOST, workAgent3);
-        appCb4.expectCallback(CallbackEntry.LOST, workAgent4);
-        appCb5.expectCallback(CallbackEntry.LOST, workAgent5);
+        appCb1.expect(CallbackEntry.LOST, workAgent1);
+        appCb2.expect(CallbackEntry.LOST, workAgent2);
+        appCb3.expect(CallbackEntry.LOST, workAgent3);
+        appCb4.expect(CallbackEntry.LOST, workAgent4);
+        appCb5.expect(CallbackEntry.LOST, workAgent5);
 
         appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent);
         appCb2.assertNoCallback();
@@ -15594,11 +15940,19 @@
         mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
         mServiceContext.setPermission(MANAGE_TEST_NETWORKS, PERMISSION_GRANTED);
 
-        // In this test the automotive feature will be enabled.
-        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+        // Has automotive feature.
+        validateAutomotiveEthernetAllowedUids(true);
+
+        // No automotive feature.
+        validateAutomotiveEthernetAllowedUids(false);
+    }
+
+    private void validateAutomotiveEthernetAllowedUids(final boolean hasAutomotiveFeature)
+            throws Exception {
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, hasAutomotiveFeature);
 
         // Simulate a restricted ethernet network.
-        final NetworkCapabilities.Builder agentNetCaps = new NetworkCapabilities.Builder()
+        final NetworkCapabilities.Builder ncb = new NetworkCapabilities.Builder()
                 .addTransportType(TRANSPORT_ETHERNET)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -15606,8 +15960,34 @@
                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
 
         mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET,
-                new LinkProperties(), agentNetCaps.build());
-        validateAllowedUids(mEthernetNetworkAgent, TRANSPORT_ETHERNET, agentNetCaps, true);
+                new LinkProperties(), ncb.build());
+
+        final ArraySet<Integer> serviceUidSet = new ArraySet<>();
+        serviceUidSet.add(TEST_PACKAGE_UID);
+
+        final TestNetworkCallback cb = new TestNetworkCallback();
+
+        mCm.requestNetwork(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_ETHERNET)
+                .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                .build(), cb);
+        mEthernetNetworkAgent.connect(true);
+        cb.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+
+        // Cell gets to set the service UID as access UID
+        ncb.setAllowedUids(serviceUidSet);
+        mEthernetNetworkAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
+        if (SdkLevel.isAtLeastT() && hasAutomotiveFeature) {
+            cb.expectCapabilitiesThat(mEthernetNetworkAgent,
+                    caps -> caps.getAllowedUids().equals(serviceUidSet));
+        } else {
+            // S and no automotive feature must ignore access UIDs.
+            cb.assertNoCallback(TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        mEthernetNetworkAgent.disconnect();
+        cb.expect(CallbackEntry.LOST, mEthernetNetworkAgent);
+        mCm.unregisterNetworkCallback(cb);
     }
 
     @Test
@@ -15621,7 +16001,7 @@
 
         // Simulate a restricted telephony network. The telephony factory is entitled to set
         // the access UID to the service package on any of its restricted networks.
-        final NetworkCapabilities.Builder agentNetCaps = new NetworkCapabilities.Builder()
+        final NetworkCapabilities.Builder ncb = new NetworkCapabilities.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -15630,13 +16010,8 @@
                 .setNetworkSpecifier(new TelephonyNetworkSpecifier(1 /* subid */));
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR,
-                new LinkProperties(), agentNetCaps.build());
-        validateAllowedUids(mCellNetworkAgent, TRANSPORT_CELLULAR, agentNetCaps, false);
-    }
+                new LinkProperties(), ncb.build());
 
-    private void validateAllowedUids(final TestNetworkAgentWrapper testAgent,
-            @NetworkCapabilities.Transport final int transportUnderTest,
-            final NetworkCapabilities.Builder ncb, final boolean forAutomotive) throws Exception {
         final ArraySet<Integer> serviceUidSet = new ArraySet<>();
         serviceUidSet.add(TEST_PACKAGE_UID);
         final ArraySet<Integer> nonServiceUidSet = new ArraySet<>();
@@ -15647,34 +16022,28 @@
 
         final TestNetworkCallback cb = new TestNetworkCallback();
 
-        /* Test setting UIDs */
         // Cell gets to set the service UID as access UID
         mCm.requestNetwork(new NetworkRequest.Builder()
-                .addTransportType(transportUnderTest)
+                .addTransportType(TRANSPORT_CELLULAR)
                 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
                 .build(), cb);
-        testAgent.connect(true);
-        cb.expectAvailableThenValidatedCallbacks(testAgent);
+        mCellNetworkAgent.connect(true);
+        cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         ncb.setAllowedUids(serviceUidSet);
-        testAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
+        mCellNetworkAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
         if (SdkLevel.isAtLeastT()) {
-            cb.expectCapabilitiesThat(testAgent,
+            cb.expectCapabilitiesThat(mCellNetworkAgent,
                     caps -> caps.getAllowedUids().equals(serviceUidSet));
         } else {
             // S must ignore access UIDs.
             cb.assertNoCallback(TEST_CALLBACK_TIMEOUT_MS);
         }
 
-        /* Test setting UIDs is rejected when expected */
-        if (forAutomotive) {
-            mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
-        }
-
         // ...but not to some other UID. Rejection sets UIDs to the empty set
         ncb.setAllowedUids(nonServiceUidSet);
-        testAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
+        mCellNetworkAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
         if (SdkLevel.isAtLeastT()) {
-            cb.expectCapabilitiesThat(testAgent,
+            cb.expectCapabilitiesThat(mCellNetworkAgent,
                     caps -> caps.getAllowedUids().isEmpty());
         } else {
             // S must ignore access UIDs.
@@ -15683,18 +16052,18 @@
 
         // ...and also not to multiple UIDs even including the service UID
         ncb.setAllowedUids(serviceUidSetPlus);
-        testAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
+        mCellNetworkAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
         cb.assertNoCallback(TEST_CALLBACK_TIMEOUT_MS);
 
-        testAgent.disconnect();
-        cb.expectCallback(CallbackEntry.LOST, testAgent);
+        mCellNetworkAgent.disconnect();
+        cb.expect(CallbackEntry.LOST, mCellNetworkAgent);
         mCm.unregisterNetworkCallback(cb);
 
         // Must be unset before touching the transports, because remove and add transport types
         // check the specifier on the builder immediately, contradicting normal builder semantics
         // TODO : fix the builder
         ncb.setNetworkSpecifier(null);
-        ncb.removeTransportType(transportUnderTest);
+        ncb.removeTransportType(TRANSPORT_CELLULAR);
         ncb.addTransportType(TRANSPORT_WIFI);
         // Wifi does not get to set access UID, even to the correct UID
         mCm.requestNetwork(new NetworkRequest.Builder()
@@ -15997,8 +16366,8 @@
         // callback with wifi network from fallback request.
         mCellNetworkAgent.disconnect();
         mDefaultNetworkCallback.assertNoCallback();
-        cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        mTestPackageDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        cellNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        mTestPackageDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
         mTestPackageDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
         inorder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(wifiConfig);
@@ -16025,7 +16394,7 @@
         // Wifi network disconnected. mTestPackageDefaultNetworkCallback should not receive
         // any callback.
         mWiFiNetworkAgent.disconnect();
-        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         mTestPackageDefaultNetworkCallback.assertNoCallback();
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(TEST_PACKAGE_UID));
@@ -16274,7 +16643,7 @@
         // Disconnect wifi
         mWiFiNetworkAgent.disconnect();
         assertNoCallbacks(mProfileDefaultNetworkCallback, mTestPackageDefaultNetworkCallback);
-        mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
         mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
     }
 
@@ -16471,21 +16840,25 @@
         assertThrows(NullPointerException.class, () -> mService.unofferNetwork(null));
     }
 
-    @Test
-    public void testIgnoreValidationAfterRoamDisabled() throws Exception {
-        assumeFalse(SdkLevel.isAtLeastT());
-        // testIgnoreValidationAfterRoam off
-        doReturn(-1).when(mResources)
+    public void doTestIgnoreValidationAfterRoam(int resValue, final boolean enabled)
+            throws Exception {
+        doReturn(resValue).when(mResources)
                 .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
 
+        final String bssid1 = "AA:AA:AA:AA:AA:AA";
+        final String bssid2 = "BB:BB:BB:BB:BB:BB";
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
         NetworkCapabilities wifiNc1 = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_VPN)
+                .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                .addCapability(NET_CAPABILITY_TRUSTED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addTransportType(TRANSPORT_WIFI)
-                .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build());
-        NetworkCapabilities wifiNc2 = new NetworkCapabilities()
-                .addTransportType(TRANSPORT_WIFI)
-                .setTransportInfo(new WifiInfo.Builder().setBssid("BB:BB:BB:BB:BB:BB").build());
+                .setTransportInfo(new WifiInfo.Builder().setBssid(bssid1).build());
+        NetworkCapabilities wifiNc2 = new NetworkCapabilities(wifiNc1)
+                .setTransportInfo(new WifiInfo.Builder().setBssid(bssid2).build());
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName(WIFI_IFNAME);
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc1);
@@ -16495,70 +16868,142 @@
         final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
         final NetworkRequest wifiRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_WIFI).build();
-        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+        mCm.requestNetwork(wifiRequest, wifiNetworkCallback);
         wifiNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         registerDefaultNetworkCallbacks();
         mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
-        // Wi-Fi roaming from wifiNc1 to wifiNc2.
+        // There is a bug in the current code where ignoring validation after roam will not
+        // correctly change the default network if the result if the validation is partial or
+        // captive portal. TODO : fix the bug and reinstate this code.
+        if (false) {
+            // Wi-Fi roaming from wifiNc1 to wifiNc2 but the network is now behind a captive portal.
+            mWiFiNetworkAgent.setNetworkCapabilities(wifiNc2, true /* sendToConnectivityService */);
+            // The only thing changed in this CAPS is the BSSID, which can't be tested for in this
+            // test because it's redacted.
+            wifiNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                    mWiFiNetworkAgent);
+            mDefaultNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                    mWiFiNetworkAgent);
+            mWiFiNetworkAgent.setNetworkPortal(TEST_REDIRECT_URL, false /* isStrictMode */);
+            mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+            // Wi-Fi is now detected to have a portal : cell should become the default network.
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+            wifiNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED,
+                    mWiFiNetworkAgent);
+            wifiNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL,
+                    mWiFiNetworkAgent);
+
+            // Wi-Fi becomes valid again. The default network goes back to Wi-Fi.
+            mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
+            mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+            wifiNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_CAPTIVE_PORTAL,
+                    mWiFiNetworkAgent);
+
+            // Wi-Fi roaming from wifiNc2 to wifiNc1, and the network now has partial connectivity.
+            mWiFiNetworkAgent.setNetworkCapabilities(wifiNc1, true);
+            wifiNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                    mWiFiNetworkAgent);
+            mDefaultNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                    mWiFiNetworkAgent);
+            mWiFiNetworkAgent.setNetworkPartial();
+            mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+            // Wi-Fi now only offers partial connectivity, so in the absence of accepting partial
+            // connectivity explicitly for this network, it loses default status to cell.
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+            wifiNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                    mWiFiNetworkAgent);
+
+            // Wi-Fi becomes valid again. The default network goes back to Wi-Fi.
+            mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
+            mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+            wifiNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                    mWiFiNetworkAgent);
+        }
+        mCm.unregisterNetworkCallback(wifiNetworkCallback);
+
+        // Wi-Fi roams from wifiNc1 to wifiNc2, and now becomes really invalid. If validation
+        // failures after roam are not ignored, this will cause cell to become the default network.
+        // If they are ignored, this will not cause a switch until later.
         mWiFiNetworkAgent.setNetworkCapabilities(wifiNc2, true);
-        mWiFiNetworkAgent.setNetworkInvalid(false);
+        mDefaultNetworkCallback.expect(CallbackEntry.NETWORK_CAPS_UPDATED,
+                mWiFiNetworkAgent);
+        mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
         mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
-        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+        if (enabled) {
+            // Network validation failed, but the result will be ignored.
+            assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
+                    NET_CAPABILITY_VALIDATED));
+            mWiFiNetworkAgent.setNetworkValid(false);
+
+            // Behavior of after config_validationFailureAfterRoamIgnoreTimeMillis
+            ConditionVariable waitForValidationBlock = new ConditionVariable();
+            doReturn(50).when(mResources)
+                    .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+            // Wi-Fi roaming from wifiNc2 to wifiNc1.
+            mWiFiNetworkAgent.setNetworkCapabilities(wifiNc1, true);
+            mWiFiNetworkAgent.setNetworkInvalid(false);
+            waitForValidationBlock.block(150);
+            mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        } else {
+            mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        }
+
+        // Wi-Fi is still connected and would become the default network if cell were to
+        // disconnect. This assertion ensures that the switch to cellular was not caused by
+        // Wi-Fi disconnecting (e.g., because the capability change to wifiNc2 caused it
+        // to stop satisfying the default request).
+        mCellNetworkAgent.disconnect();
+        mDefaultNetworkCallback.expect(CallbackEntry.LOST, mCellNetworkAgent);
+        mDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+    }
+
+    @Test
+    public void testIgnoreValidationAfterRoamDisabled() throws Exception {
+        doTestIgnoreValidationAfterRoam(-1, false /* enabled */);
     }
 
     @Test
     public void testIgnoreValidationAfterRoamEnabled() throws Exception {
-        assumeFalse(SdkLevel.isAtLeastT());
-        // testIgnoreValidationAfterRoam on
-        doReturn(5000).when(mResources)
-                .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
-
-        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        mCellNetworkAgent.connect(true);
-        NetworkCapabilities wifiNc1 = new NetworkCapabilities()
-                .addTransportType(TRANSPORT_WIFI)
-                .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build());
-        NetworkCapabilities wifiNc2 = new NetworkCapabilities()
-                .addTransportType(TRANSPORT_WIFI)
-                .setTransportInfo(new WifiInfo.Builder().setBssid("BB:BB:BB:BB:BB:BB").build());
-        final LinkProperties wifiLp = new LinkProperties();
-        wifiLp.setInterfaceName(WIFI_IFNAME);
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc1);
-        mWiFiNetworkAgent.connect(true);
-
-        // The default network will be switching to Wi-Fi Network.
-        final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
-        final NetworkRequest wifiRequest = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_WIFI).build();
-        mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
-        wifiNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-        registerDefaultNetworkCallbacks();
-        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
-
-        // Wi-Fi roaming from wifiNc1 to wifiNc2.
-        mWiFiNetworkAgent.setNetworkCapabilities(wifiNc2, true);
-        mWiFiNetworkAgent.setNetworkInvalid(false);
-        mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
-
-        // Network validation failed, but the result will be ignored.
-        assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
-                NET_CAPABILITY_VALIDATED));
-        mWiFiNetworkAgent.setNetworkValid(false);
-
-        // Behavior of after config_validationFailureAfterRoamIgnoreTimeMillis
-        ConditionVariable waitForValidationBlock = new ConditionVariable();
-        doReturn(50).when(mResources)
-                .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
-        // Wi-Fi roaming from wifiNc2 to wifiNc1.
-        mWiFiNetworkAgent.setNetworkCapabilities(wifiNc1, true);
-        mWiFiNetworkAgent.setNetworkInvalid(false);
-        waitForValidationBlock.block(150);
-        mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
-        mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        final boolean enabled = !SdkLevel.isAtLeastT();
+        doTestIgnoreValidationAfterRoam(5_000, enabled);
     }
 
     @Test
+    public void testShouldIgnoreValidationFailureAfterRoam() {
+        // Always disabled on T+.
+        assumeFalse(SdkLevel.isAtLeastT());
+
+        NetworkAgentInfo nai = fakeWifiNai(new NetworkCapabilities());
+
+        // Enabled, but never roamed.
+        doReturn(5_000).when(mResources)
+                .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+        assertEquals(0, nai.lastRoamTime);
+        assertFalse(mService.shouldIgnoreValidationFailureAfterRoam(nai));
+
+        // Roamed recently.
+        nai.lastRoamTime = SystemClock.elapsedRealtime() - 500 /* ms */;
+        assertTrue(mService.shouldIgnoreValidationFailureAfterRoam(nai));
+
+        // Disabled due to invalid setting (maximum is 10 seconds).
+        doReturn(15_000).when(mResources)
+                .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+        assertFalse(mService.shouldIgnoreValidationFailureAfterRoam(nai));
+
+        // Disabled.
+        doReturn(-1).when(mResources)
+                .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+        assertFalse(mService.shouldIgnoreValidationFailureAfterRoam(nai));
+    }
+
+
+    @Test
     public void testLegacyTetheringApiGuardWithProperPermission() throws Exception {
         final String testIface = "test0";
         mServiceContext.setPermission(ACCESS_NETWORK_STATE, PERMISSION_DENIED);
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 9401d47..624071a 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -748,6 +748,13 @@
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
         verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
+
+        for (int direction : new int[] {DIRECTION_OUT, DIRECTION_IN, DIRECTION_FWD}) {
+            verify(mMockNetd, times(ADDRESS_FAMILIES.length))
+                    .ipSecDeleteSecurityPolicy(
+                            anyInt(), anyInt(), eq(direction), anyInt(), anyInt(), anyInt());
+        }
+
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
diff --git a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
deleted file mode 100644
index 7688a6b..0000000
--- a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2012 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.server;
-
-import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
-import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
-import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
-import static android.util.DebugUtils.valueToString;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.INetd;
-import android.net.INetdUnsolicitedEventListener;
-import android.net.LinkAddress;
-import android.net.NetworkPolicyManager;
-import android.os.BatteryStats;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.ArrayMap;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.server.NetworkManagementService.Dependencies;
-import com.android.server.net.BaseNetworkObserver;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.BiFunction;
-
-/**
- * Tests for {@link NetworkManagementService}.
- */
-@RunWith(DevSdkIgnoreRunner.class)
-@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-public class NetworkManagementServiceTest {
-    private NetworkManagementService mNMService;
-    @Mock private Context mContext;
-    @Mock private ConnectivityManager mCm;
-    @Mock private IBatteryStats.Stub mBatteryStatsService;
-    @Mock private INetd.Stub mNetdService;
-
-    private static final int TEST_UID = 111;
-
-    @NonNull
-    @Captor
-    private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
-
-    private final MockDependencies mDeps = new MockDependencies();
-
-    private final class MockDependencies extends Dependencies {
-        @Override
-        public IBinder getService(String name) {
-            switch (name) {
-                case BatteryStats.SERVICE_NAME:
-                    return mBatteryStatsService;
-                default:
-                    throw new UnsupportedOperationException("Unknown service " + name);
-            }
-        }
-
-        @Override
-        public void registerLocalService(NetworkManagementInternal nmi) {
-        }
-
-        @Override
-        public INetd getNetd() {
-            return mNetdService;
-        }
-
-        @Override
-        public int getCallingUid() {
-            return Process.SYSTEM_UID;
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        doNothing().when(mNetdService)
-                .registerUnsolicitedEventListener(mUnsolListenerCaptor.capture());
-        doReturn(Context.CONNECTIVITY_SERVICE).when(mContext).getSystemServiceName(
-                eq(ConnectivityManager.class));
-        doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE));
-        // Start the service and wait until it connects to our socket.
-        mNMService = NetworkManagementService.create(mContext, mDeps);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mNMService.shutdown();
-    }
-
-    private static <T> T expectSoon(T mock) {
-        return verify(mock, timeout(200));
-    }
-
-    /**
-     * Tests that network observers work properly.
-     */
-    @Test
-    public void testNetworkObservers() throws Exception {
-        BaseNetworkObserver observer = mock(BaseNetworkObserver.class);
-        doReturn(new Binder()).when(observer).asBinder();  // Used by registerObserver.
-        mNMService.registerObserver(observer);
-
-        // Forget everything that happened to the mock so far, so we can explicitly verify
-        // everything that happens and does not happen to it from now on.
-
-        INetdUnsolicitedEventListener unsolListener = mUnsolListenerCaptor.getValue();
-        reset(observer);
-        // Now call unsolListener methods and ensure that the observer methods are
-        // called. After every method we expect a callback soon after; to ensure that
-        // invalid messages don't cause any callbacks, we call verifyNoMoreInteractions at the end.
-
-        /**
-         * Interface changes.
-         */
-        unsolListener.onInterfaceAdded("rmnet12");
-        expectSoon(observer).interfaceAdded("rmnet12");
-
-        unsolListener.onInterfaceRemoved("eth1");
-        expectSoon(observer).interfaceRemoved("eth1");
-
-        unsolListener.onInterfaceChanged("clat4", true);
-        expectSoon(observer).interfaceStatusChanged("clat4", true);
-
-        unsolListener.onInterfaceLinkStateChanged("rmnet0", false);
-        expectSoon(observer).interfaceLinkStateChanged("rmnet0", false);
-
-        /**
-         * Bandwidth control events.
-         */
-        unsolListener.onQuotaLimitReached("data", "rmnet_usb0");
-        expectSoon(observer).limitReached("data", "rmnet_usb0");
-
-        /**
-         * Interface class activity.
-         */
-        unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, TEST_UID);
-        expectSoon(observer).interfaceClassDataActivityChanged(1, true, 1234, TEST_UID);
-
-        unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, TEST_UID);
-        expectSoon(observer).interfaceClassDataActivityChanged(9, false, 5678, TEST_UID);
-
-        unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, TEST_UID);
-        expectSoon(observer).interfaceClassDataActivityChanged(9, false, 4321, TEST_UID);
-
-        /**
-         * IP address changes.
-         */
-        unsolListener.onInterfaceAddressUpdated("fe80::1/64", "wlan0", 128, 253);
-        expectSoon(observer).addressUpdated("wlan0", new LinkAddress("fe80::1/64", 128, 253));
-
-        unsolListener.onInterfaceAddressRemoved("fe80::1/64", "wlan0", 128, 253);
-        expectSoon(observer).addressRemoved("wlan0", new LinkAddress("fe80::1/64", 128, 253));
-
-        unsolListener.onInterfaceAddressRemoved("2001:db8::1/64", "wlan0", 1, 0);
-        expectSoon(observer).addressRemoved("wlan0", new LinkAddress("2001:db8::1/64", 1, 0));
-
-        /**
-         * DNS information broadcasts.
-         */
-        unsolListener.onInterfaceDnsServerInfo("rmnet_usb0", 3600, new String[]{"2001:db8::1"});
-        expectSoon(observer).interfaceDnsServerInfo("rmnet_usb0", 3600,
-                new String[]{"2001:db8::1"});
-
-        unsolListener.onInterfaceDnsServerInfo("wlan0", 14400,
-                new String[]{"2001:db8::1", "2001:db8::2"});
-        expectSoon(observer).interfaceDnsServerInfo("wlan0", 14400,
-                new String[]{"2001:db8::1", "2001:db8::2"});
-
-        // We don't check for negative lifetimes, only for parse errors.
-        unsolListener.onInterfaceDnsServerInfo("wlan0", -3600, new String[]{"::1"});
-        expectSoon(observer).interfaceDnsServerInfo("wlan0", -3600,
-                new String[]{"::1"});
-
-        // No syntax checking on the addresses.
-        unsolListener.onInterfaceDnsServerInfo("wlan0", 600,
-                new String[]{"", "::", "", "foo", "::1"});
-        expectSoon(observer).interfaceDnsServerInfo("wlan0", 600,
-                new String[]{"", "::", "", "foo", "::1"});
-
-        // Make sure nothing else was called.
-        verifyNoMoreInteractions(observer);
-    }
-
-    @Test
-    public void testFirewallEnabled() {
-        mNMService.setFirewallEnabled(true);
-        assertTrue(mNMService.isFirewallEnabled());
-
-        mNMService.setFirewallEnabled(false);
-        assertFalse(mNMService.isFirewallEnabled());
-    }
-
-    @Test
-    public void testNetworkRestrictedDefault() {
-        assertFalse(mNMService.isNetworkRestricted(TEST_UID));
-    }
-
-    @Test
-    public void testMeteredNetworkRestrictions() throws RemoteException {
-        // Make sure the mocked netd method returns true.
-        doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
-
-        // Restrict usage of mobile data in background
-        mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, true);
-        assertTrue("Should be true since mobile data usage is restricted",
-                mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
-
-        mNMService.setDataSaverModeEnabled(true);
-        verify(mNetdService).bandwidthEnableDataSaver(true);
-
-        mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false);
-        assertTrue("Should be true since data saver is on and the uid is not allowlisted",
-                mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).removeUidFromMeteredNetworkDenyList(TEST_UID);
-
-        mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, true);
-        assertFalse("Should be false since data saver is on and the uid is allowlisted",
-                mNMService.isNetworkRestricted(TEST_UID));
-        verify(mCm).addUidToMeteredNetworkAllowList(TEST_UID);
-
-        // remove uid from allowlist and turn datasaver off again
-        mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
-        verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
-        mNMService.setDataSaverModeEnabled(false);
-        verify(mNetdService).bandwidthEnableDataSaver(false);
-        assertFalse("Network should not be restricted when data saver is off",
-                mNMService.isNetworkRestricted(TEST_UID));
-    }
-
-    @Test
-    public void testFirewallChains() {
-        final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
-        // Dozable chain
-        final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>();
-        isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
-        isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false);
-        isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true);
-        expected.put(FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable);
-        // Powersaver chain
-        final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>();
-        isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
-        isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false);
-        isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true);
-        expected.put(FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave);
-        // Standby chain
-        final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>();
-        isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false);
-        isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false);
-        isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true);
-        expected.put(FIREWALL_CHAIN_STANDBY, isRestrictedForStandby);
-        // Restricted mode chain
-        final ArrayMap<Integer, Boolean> isRestrictedForRestrictedMode = new ArrayMap<>();
-        isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
-        isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false);
-        isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true);
-        expected.put(FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode);
-        // Low Power Standby chain
-        final ArrayMap<Integer, Boolean> isRestrictedForLowPowerStandby = new ArrayMap<>();
-        isRestrictedForLowPowerStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
-        isRestrictedForLowPowerStandby.put(INetd.FIREWALL_RULE_ALLOW, false);
-        isRestrictedForLowPowerStandby.put(INetd.FIREWALL_RULE_DENY, true);
-        expected.put(FIREWALL_CHAIN_LOW_POWER_STANDBY, isRestrictedForLowPowerStandby);
-
-        final int[] chains = {
-                FIREWALL_CHAIN_STANDBY,
-                FIREWALL_CHAIN_POWERSAVE,
-                FIREWALL_CHAIN_DOZABLE,
-                FIREWALL_CHAIN_RESTRICTED,
-                FIREWALL_CHAIN_LOW_POWER_STANDBY
-        };
-        final int[] states = {
-                INetd.FIREWALL_RULE_ALLOW,
-                INetd.FIREWALL_RULE_DENY,
-                NetworkPolicyManager.FIREWALL_RULE_DEFAULT
-        };
-        BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> {
-            return String.format("Unexpected value for chain: %s and state: %s",
-                    valueToString(INetd.class, "FIREWALL_CHAIN_", chain),
-                    valueToString(INetd.class, "FIREWALL_RULE_", state));
-        };
-        for (int chain : chains) {
-            final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain);
-            mNMService.setFirewallChainEnabled(chain, true);
-            verify(mCm).setFirewallChainEnabled(chain, true /* enabled */);
-            for (int state : states) {
-                mNMService.setFirewallUidRule(chain, TEST_UID, state);
-                assertEquals(errorMsg.apply(chain, state),
-                        expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID));
-            }
-            mNMService.setFirewallChainEnabled(chain, false);
-            verify(mCm).setFirewallChainEnabled(chain, false /* enabled */);
-        }
-    }
-}
diff --git a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
index 157507b..3849e49 100644
--- a/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/CarrierPrivilegeAuthenticatorTest.java
@@ -20,18 +20,17 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
 
-import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
@@ -40,14 +39,15 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.net.NetworkRequest;
-import android.net.NetworkSpecifier;
+import android.net.NetworkCapabilities;
 import android.net.TelephonyNetworkSpecifier;
+import android.os.Build;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
+import com.android.net.module.util.CollectionUtils;
 import com.android.networkstack.apishim.TelephonyManagerShimImpl;
-import com.android.networkstack.apishim.common.TelephonyManagerShim;
+import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim;
 import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.DevSdkIgnoreRunner;
@@ -58,16 +58,16 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.Collections;
-import java.util.List;
+import java.util.Map;
 
 /**
  * Tests for CarrierPrivilegeAuthenticatorTest.
  *
  * Build, install and run with:
- *  runtest frameworks-net -c com.android.server.connectivity.CarrierPrivilegeAuthenticatorTest
+ *  atest FrameworksNetTests:CarrierPrivilegeAuthenticatorTest
  */
 @RunWith(DevSdkIgnoreRunner.class)
-@IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
 public class CarrierPrivilegeAuthenticatorTest {
     private static final int SUBSCRIPTION_COUNT = 2;
     private static final int TEST_SUBSCRIPTION_ID = 1;
@@ -107,8 +107,7 @@
         doReturn(mPackageManager).when(mContext).getPackageManager();
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = mCarrierConfigPkgUid;
-        doReturn(applicationInfo).when(mPackageManager)
-                .getApplicationInfo(eq(mTestPkg), anyInt());
+        doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(mTestPkg), anyInt());
         mCarrierPrivilegeAuthenticator =
                 new TestCarrierPrivilegeAuthenticator(mContext, mTelephonyManager);
     }
@@ -119,21 +118,23 @@
         return captor.getValue();
     }
 
-    private List<TelephonyManagerShim.CarrierPrivilegesListenerShim>
-            getCarrierPrivilegesListeners() {
-        final ArgumentCaptor<TelephonyManagerShim.CarrierPrivilegesListenerShim> captor =
-                ArgumentCaptor.forClass(TelephonyManagerShim.CarrierPrivilegesListenerShim.class);
+    private Map<Integer, CarrierPrivilegesListenerShim> getCarrierPrivilegesListeners() {
+        final ArgumentCaptor<Integer> slotCaptor = ArgumentCaptor.forClass(Integer.class);
+        final ArgumentCaptor<CarrierPrivilegesListenerShim> listenerCaptor =
+                ArgumentCaptor.forClass(CarrierPrivilegesListenerShim.class);
         try {
-            verify(mTelephonyManagerShim, atLeastOnce())
-                    .addCarrierPrivilegesListener(anyInt(), any(), captor.capture());
+            verify(mTelephonyManagerShim, atLeastOnce()).addCarrierPrivilegesListener(
+                    slotCaptor.capture(), any(), listenerCaptor.capture());
         } catch (UnsupportedApiLevelException e) {
         }
-        return captor.getAllValues();
+        final Map<Integer, CarrierPrivilegesListenerShim> result =
+                CollectionUtils.assoc(slotCaptor.getAllValues(), listenerCaptor.getAllValues());
+        clearInvocations(mTelephonyManagerShim);
+        return result;
     }
 
     private Intent buildTestMultiSimConfigBroadcastIntent() {
-        final Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
-        return intent;
+        return new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
     }
     @Test
     public void testConstructor() throws Exception {
@@ -146,99 +147,96 @@
         assertEquals(1, filter.countActions());
         assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
 
-        verify(mTelephonyManagerShim, times(2))
-                .addCarrierPrivilegesListener(anyInt(), any(), any());
-        verify(mTelephonyManagerShim)
-                .addCarrierPrivilegesListener(eq(0), any(), any());
-        verify(mTelephonyManagerShim)
-                .addCarrierPrivilegesListener(eq(1), any(), any());
-        assertEquals(2, getCarrierPrivilegesListeners().size());
+        // Two listeners originally registered, one for slot 0 and one for slot 1
+        final Map<Integer, CarrierPrivilegesListenerShim> initialListeners =
+                getCarrierPrivilegesListeners();
+        assertNotNull(initialListeners.get(0));
+        assertNotNull(initialListeners.get(1));
+        assertEquals(2, initialListeners.size());
 
-        final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
-                new TelephonyNetworkSpecifier(0);
-        final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
-        networkRequestBuilder.addTransportType(TRANSPORT_CELLULAR);
-        networkRequestBuilder.setNetworkSpecifier(telephonyNetworkSpecifier);
+        final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
 
         assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, ncBuilder.build()));
         assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid + 1, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid + 1, ncBuilder.build()));
     }
 
     @Test
     public void testMultiSimConfigChanged() throws Exception {
-        doReturn(1).when(mTelephonyManager).getActiveModemCount();
-        final List<TelephonyManagerShim.CarrierPrivilegesListenerShim> carrierPrivilegesListeners =
+        // Two listeners originally registered, one for slot 0 and one for slot 1
+        final Map<Integer, CarrierPrivilegesListenerShim> initialListeners =
                 getCarrierPrivilegesListeners();
+        assertNotNull(initialListeners.get(0));
+        assertNotNull(initialListeners.get(1));
+        assertEquals(2, initialListeners.size());
 
+        doReturn(1).when(mTelephonyManager).getActiveModemCount();
         mCarrierPrivilegeAuthenticator.onReceive(
                 mContext, buildTestMultiSimConfigBroadcastIntent());
-        for (TelephonyManagerShim.CarrierPrivilegesListenerShim carrierPrivilegesListener
-                : carrierPrivilegesListeners) {
-            verify(mTelephonyManagerShim)
-                    .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
+        // Check all listeners have been removed
+        for (CarrierPrivilegesListenerShim listener : initialListeners.values()) {
+            verify(mTelephonyManagerShim).removeCarrierPrivilegesListener(eq(listener));
         }
 
         // Expect a new CarrierPrivilegesListener to have been registered for slot 0, and none other
-        // (2 previously registered during startup, for slots 0 & 1)
-        verify(mTelephonyManagerShim, times(3))
-                .addCarrierPrivilegesListener(anyInt(), any(), any());
-        verify(mTelephonyManagerShim, times(2))
-                .addCarrierPrivilegesListener(eq(0), any(), any());
+        final Map<Integer, CarrierPrivilegesListenerShim> newListeners =
+                getCarrierPrivilegesListeners();
+        assertNotNull(newListeners.get(0));
+        assertEquals(1, newListeners.size());
 
-        final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
-                new TelephonyNetworkSpecifier(0);
-        final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
-        networkRequestBuilder.addTransportType(TRANSPORT_CELLULAR);
-        networkRequestBuilder.setNetworkSpecifier(telephonyNetworkSpecifier);
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(0);
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(specifier)
+                .build();
         assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, nc));
         assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid + 1, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid + 1, nc));
     }
 
     @Test
     public void testOnCarrierPrivilegesChanged() throws Exception {
-        final TelephonyManagerShim.CarrierPrivilegesListenerShim listener =
-                getCarrierPrivilegesListeners().get(0);
+        final CarrierPrivilegesListenerShim listener = getCarrierPrivilegesListeners().get(0);
 
-        final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
-                new TelephonyNetworkSpecifier(0);
-        final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
-        networkRequestBuilder.addTransportType(TRANSPORT_CELLULAR);
-        networkRequestBuilder.setNetworkSpecifier(telephonyNetworkSpecifier);
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(0);
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(specifier)
+                .build();
 
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = mCarrierConfigPkgUid + 1;
-        doReturn(applicationInfo).when(mPackageManager)
-                .getApplicationInfo(eq(mTestPkg), anyInt());
+        doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(mTestPkg), anyInt());
         listener.onCarrierPrivilegesChanged(Collections.emptyList(), new int[] {});
 
         assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, nc));
         assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid + 1, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid + 1, nc));
     }
 
     @Test
     public void testDefaultSubscription() throws Exception {
-        final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
-        networkRequestBuilder.addTransportType(TRANSPORT_CELLULAR);
+        final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
+        ncBuilder.addTransportType(TRANSPORT_CELLULAR);
         assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, ncBuilder.build()));
 
-        networkRequestBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
+        ncBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
         assertTrue(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, ncBuilder.build()));
 
-        // The builder for NetworkRequest doesn't allow removing the transport as long as a
+        // The builder for NetworkCapabilities doesn't allow removing the transport as long as a
         // specifier is set, so unset it first. TODO : fix the builder
-        networkRequestBuilder.setNetworkSpecifier((NetworkSpecifier) null);
-        networkRequestBuilder.removeTransportType(TRANSPORT_CELLULAR);
-        networkRequestBuilder.addTransportType(TRANSPORT_WIFI);
-        networkRequestBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
+        ncBuilder.setNetworkSpecifier(null);
+        ncBuilder.removeTransportType(TRANSPORT_CELLULAR);
+        ncBuilder.addTransportType(TRANSPORT_WIFI);
+        ncBuilder.setNetworkSpecifier(new TelephonyNetworkSpecifier(0));
         assertFalse(mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
-                mCarrierConfigPkgUid, networkRequestBuilder.build().networkCapabilities));
+                mCarrierConfigPkgUid, ncBuilder.build()));
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 7646c19..b651c33 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -220,12 +220,12 @@
          */
         @Override
         public String generateIpv6Address(@NonNull String iface, @NonNull String v4,
-                @NonNull String prefix64) throws IOException {
+                @NonNull String prefix64, int mark) throws IOException {
             if (BASE_IFACE.equals(iface) && XLAT_LOCAL_IPV4ADDR_STRING.equals(v4)
-                    && NAT64_PREFIX_STRING.equals(prefix64)) {
+                    && NAT64_PREFIX_STRING.equals(prefix64) && MARK == mark) {
                 return XLAT_LOCAL_IPV6ADDR_STRING;
             }
-            fail("unsupported args: " + iface + ", " + v4 + ", " + prefix64);
+            fail("unsupported args: " + iface + ", " + v4 + ", " + prefix64 + ", " + mark);
             return null;
         }
 
@@ -417,7 +417,7 @@
 
         // Generate a checksum-neutral IID.
         inOrder.verify(mDeps).generateIpv6Address(eq(BASE_IFACE),
-                eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(NAT64_PREFIX_STRING));
+                eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(NAT64_PREFIX_STRING), eq(MARK));
 
         // Open, configure and bring up the tun interface.
         inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE));
@@ -516,28 +516,38 @@
         assertEquals(65508, ClatCoordinator.adjustMtu(CLAT_MAX_MTU + 1 /* over maximum mtu */));
     }
 
-    @Test
-    public void testDump() throws Exception {
-        final ClatCoordinator coordinator = makeClatCoordinator();
+    private void verifyDump(final ClatCoordinator coordinator, boolean clatStarted) {
         final StringWriter stringWriter = new StringWriter();
         final IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ");
-        coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
         coordinator.dump(ipw);
 
         final String[] dumpStrings = stringWriter.toString().split("\n");
-        assertEquals(6, dumpStrings.length);
-        assertEquals("CLAT tracker: iface: test0 (1000), v4iface: v4-test0 (1001), "
-                + "v4: /192.0.0.46, v6: /2001:db8:0:b11::464, pfx96: /64:ff9b::, "
-                + "pid: 10483, cookie: 27149", dumpStrings[0].trim());
-        assertEquals("Forwarding rules:", dumpStrings[1].trim());
-        assertEquals("BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif",
-                dumpStrings[2].trim());
-        assertEquals("1000 /64:ff9b::/96 /2001:db8:0:b11::464 -> /192.0.0.46 1001",
-                dumpStrings[3].trim());
-        assertEquals("BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif",
-                dumpStrings[4].trim());
-        assertEquals("1001 /192.0.0.46 -> /2001:db8:0:b11::464 /64:ff9b::/96 1000 ether",
-                dumpStrings[5].trim());
+        if (clatStarted) {
+            assertEquals(6, dumpStrings.length);
+            assertEquals("CLAT tracker: iface: test0 (1000), v4iface: v4-test0 (1001), "
+                    + "v4: /192.0.0.46, v6: /2001:db8:0:b11::464, pfx96: /64:ff9b::, "
+                    + "pid: 10483, cookie: 27149", dumpStrings[0].trim());
+            assertEquals("Forwarding rules:", dumpStrings[1].trim());
+            assertEquals("BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif",
+                    dumpStrings[2].trim());
+            assertEquals("1000 /64:ff9b::/96 /2001:db8:0:b11::464 -> /192.0.0.46 1001",
+                    dumpStrings[3].trim());
+            assertEquals("BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif",
+                    dumpStrings[4].trim());
+            assertEquals("1001 /192.0.0.46 -> /2001:db8:0:b11::464 /64:ff9b::/96 1000 ether",
+                    dumpStrings[5].trim());
+        } else {
+            assertEquals(1, dumpStrings.length);
+            assertEquals("<not started>", dumpStrings[0].trim());
+        }
+    }
+
+    @Test
+    public void testDump() throws Exception {
+        final ClatCoordinator coordinator = makeClatCoordinator();
+        verifyDump(coordinator, false /* clatStarted */);
+        coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
+        verifyDump(coordinator, true /* clatStarted */);
     }
 
     @Test
@@ -548,25 +558,18 @@
                 () -> coordinator.clatStart(BASE_IFACE, NETID, invalidPrefix));
     }
 
-    private void assertStartClat(final TestDependencies deps) throws Exception {
-        final ClatCoordinator coordinator = new ClatCoordinator(deps);
-        assertNotNull(coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX));
-    }
-
-    private void assertNotStartClat(final TestDependencies deps) {
-        // Expect that the injection function of TestDependencies causes clatStart() failed.
-        final ClatCoordinator coordinator = new ClatCoordinator(deps);
-        assertThrows(IOException.class,
-                () -> coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX));
-    }
-
     private void checkNotStartClat(final TestDependencies deps, final boolean needToCloseTunFd,
             final boolean needToClosePacketSockFd, final boolean needToCloseRawSockFd)
             throws Exception {
-        // [1] Expect that modified TestDependencies can't start clatd.
-        // Use precise check to make sure that there is no unexpected file descriptor closing.
         clearInvocations(TUN_PFD, RAW_SOCK_PFD, PACKET_SOCK_PFD);
-        assertNotStartClat(deps);
+
+        // [1] Expect that modified TestDependencies can't start clatd.
+        // Expect that the injection function of TestDependencies causes clatStart() failed.
+        final ClatCoordinator coordinatorWithBrokenDeps = new ClatCoordinator(deps);
+        assertThrows(IOException.class,
+                () -> coordinatorWithBrokenDeps.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX));
+
+        // Use precise check to make sure that there is no unexpected file descriptor closing.
         if (needToCloseTunFd) {
             verify(TUN_PFD).close();
         } else {
@@ -583,10 +586,15 @@
             verify(RAW_SOCK_PFD, never()).close();
         }
 
+        // Check that dump doesn't crash after any clat starting failure.
+        verifyDump(coordinatorWithBrokenDeps, false /* clatStarted */);
+
         // [2] Expect that unmodified TestDependencies can start clatd.
         // Used to make sure that the above modified TestDependencies has really broken the
         // clatd starting.
-        assertStartClat(new TestDependencies());
+        final ClatCoordinator coordinatorWithDefaultDeps = new ClatCoordinator(
+                new TestDependencies());
+        assertNotNull(coordinatorWithDefaultDeps.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX));
     }
 
     // The following testNotStartClat* tests verifies bunches of code for unwinding the
@@ -609,7 +617,7 @@
         class FailureDependencies extends TestDependencies {
             @Override
             public String generateIpv6Address(@NonNull String iface, @NonNull String v4,
-                    @NonNull String prefix64) throws IOException {
+                    @NonNull String prefix64, int mark) throws IOException {
                 throw new IOException();
             }
         }
diff --git a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
index a194131..3520c5b 100644
--- a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
@@ -29,6 +29,7 @@
 import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
 import com.android.server.connectivity.FullScore.MIN_CS_MANAGED_POLICY
 import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
+import com.android.server.connectivity.FullScore.POLICY_EVER_EVALUATED
 import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED
 import com.android.server.connectivity.FullScore.POLICY_IS_DESTROYED
 import com.android.server.connectivity.FullScore.POLICY_IS_UNMETERED
@@ -56,6 +57,7 @@
         vpn: Boolean = false,
         onceChosen: Boolean = false,
         acceptUnvalidated: Boolean = false,
+        everEvaluated: Boolean = true,
         destroyed: Boolean = false
     ): FullScore {
         val nac = NetworkAgentConfig.Builder().apply {
@@ -66,7 +68,8 @@
             if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
             if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
         }.build()
-        return mixInScore(nc, nac, validated, false /* yieldToBadWifi */, destroyed)
+        return mixInScore(nc, nac, validated, false /* avoidUnvalidated */,
+                false /* yieldToBadWifi */, everEvaluated, destroyed)
     }
 
     private val TAG = this::class.simpleName
@@ -122,6 +125,7 @@
         assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED))
         assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED))
         assertTrue(ns.withPolicies(destroyed = true).hasPolicy(POLICY_IS_DESTROYED))
+        assertTrue(ns.withPolicies(everEvaluated = true).hasPolicy(POLICY_EVER_EVALUATED))
     }
 
     @Test
@@ -148,4 +152,16 @@
         assertNotEquals(ns3, ns1)
         assertFalse(ns1.equals(ns4))
     }
+
+    @Test
+    fun testDescribeDifferences() {
+        val ns1 = FullScore((1L shl POLICY_EVER_EVALUATED) or (1L shl POLICY_IS_VALIDATED),
+                KEEP_CONNECTED_NONE)
+        val ns2 = FullScore((1L shl POLICY_IS_VALIDATED) or (1L shl POLICY_IS_VPN) or
+                (1L shl POLICY_IS_DESTROYED), KEEP_CONNECTED_NONE)
+        assertEquals("-EVER_EVALUATED+IS_DESTROYED+IS_VPN",
+                ns2.describeDifferencesFrom(ns1))
+        assertEquals("-IS_DESTROYED-IS_VPN+EVER_EVALUATED",
+                ns1.describeDifferencesFrom(ns2))
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
index 01249a1..0d371fa 100644
--- a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -30,6 +31,7 @@
 
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityResources;
@@ -85,12 +87,14 @@
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
     @Mock QosCallbackTracker mQosCallbackTracker;
+    @Mock PackageManager mPackageManager;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
+        doReturn(mPackageManager).when(mCtx).getPackageManager();
         ConnectivityResources.setResourcesContextForTest(mCtx);
 
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
diff --git a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
similarity index 74%
rename from tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
rename to tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
index 576b8d3..b52e8a8 100644
--- a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.util
+package com.android.server.connectivity
 
 import android.content.Context
 import android.content.res.Resources
@@ -24,8 +24,10 @@
 import android.net.ConnectivityResources
 import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI
 import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE
-import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
+import com.android.server.connectivity.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
 import android.os.Build
+import android.os.Handler
+import android.os.test.TestLooper
 import android.provider.Settings
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
@@ -34,18 +36,18 @@
 import androidx.test.filters.SmallTest
 import com.android.connectivity.resources.R
 import com.android.internal.util.test.FakeSettingsProvider
+import com.android.modules.utils.build.SdkLevel
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.argThat
-import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.any
 import org.mockito.Mockito.doCallRealMethod
 import org.mockito.Mockito.doReturn
@@ -53,20 +55,21 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
+const val HANDLER_TIMEOUT_MS = 400
+
 /**
  * Tests for [MultinetworkPolicyTracker].
  *
  * Build, install and run with:
- * atest android.net.util.MultinetworkPolicyTrackerTest
+ * atest FrameworksNetTest:MultinetworkPolicyTrackerTest
  */
 @RunWith(DevSdkIgnoreRunner::class)
 @SmallTest
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
 class MultinetworkPolicyTrackerTest {
     private val resources = mock(Resources::class.java).also {
-        doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier(
-                eq("config_networkAvoidBadWifi"), eq("integer"), any())
         doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
+        doReturn(0).`when`(it).getInteger(R.integer.config_activelyPreferBadWifi)
     }
     private val telephonyManager = mock(TelephonyManager::class.java)
     private val subscriptionManager = mock(SubscriptionManager::class.java).also {
@@ -90,7 +93,11 @@
         Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
         ConnectivityResources.setResourcesContextForTest(it)
     }
-    private val tracker = MultinetworkPolicyTracker(context, null /* handler */)
+    private val csLooper = TestLooper()
+    private val handler = Handler(csLooper.looper)
+    private val trackerDependencies = MultinetworkPolicyTrackerTestDependencies(resources)
+    private val tracker = MultinetworkPolicyTracker(context, handler,
+            null /* avoidBadWifiCallback */, trackerDependencies)
 
     private fun assertMultipathPreference(preference: Int) {
         Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
@@ -99,6 +106,11 @@
         assertEquals(preference, tracker.meteredMultipathPreference)
     }
 
+    @Before
+    fun setUp() {
+        tracker.start()
+    }
+
     @After
     fun tearDown() {
         ConnectivityResources.setResourcesContextForTest(null)
@@ -113,6 +125,7 @@
 
     @Test
     fun testUpdateAvoidBadWifi() {
+        doReturn(0).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
         Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
         assertTrue(tracker.updateAvoidBadWifi())
         assertFalse(tracker.avoidBadWifi)
@@ -120,6 +133,36 @@
         doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
         assertTrue(tracker.updateAvoidBadWifi())
         assertTrue(tracker.avoidBadWifi)
+
+        if (SdkLevel.isAtLeastU()) {
+            // On U+, the system always prefers bad wifi.
+            assertTrue(tracker.activelyPreferBadWifi)
+        } else {
+            assertFalse(tracker.activelyPreferBadWifi)
+        }
+
+        doReturn(1).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
+        if (SdkLevel.isAtLeastU()) {
+            // On U+, this didn't change the setting
+            assertFalse(tracker.updateAvoidBadWifi())
+        } else {
+            // On T-, this must have changed the setting
+            assertTrue(tracker.updateAvoidBadWifi())
+        }
+        // In all cases, now the system actively prefers bad wifi
+        assertTrue(tracker.activelyPreferBadWifi)
+
+        // Remaining tests are only useful on T-, which support both the old and new mode.
+        if (SdkLevel.isAtLeastU()) return
+
+        doReturn(0).`when`(resources).getInteger(R.integer.config_activelyPreferBadWifi)
+        assertTrue(tracker.updateAvoidBadWifi())
+        assertFalse(tracker.activelyPreferBadWifi)
+
+        // Simulate update of device config
+        trackerDependencies.putConfigActivelyPreferBadWifi(1)
+        csLooper.dispatchAll()
+        assertTrue(tracker.activelyPreferBadWifi)
     }
 
     @Test
@@ -138,6 +181,8 @@
         Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
                 MULTIPATH_PREFERENCE_PERFORMANCE.toString())
 
+        assertTrue(tracker.avoidBadWifi)
+
         val listenerCaptor = ArgumentCaptor.forClass(
                 ActiveDataSubscriptionIdListener::class.java)
         verify(telephonyManager, times(1))
@@ -145,10 +190,6 @@
         val listener = listenerCaptor.value
         listener.onActiveDataSubscriptionIdChanged(testSubId)
 
-        // Check it get resource value with test sub id.
-        verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId)
-        verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 })
-
         // Check if avoidBadWifi and meteredMultipathPreference values have been updated.
         assertFalse(tracker.avoidBadWifi)
         assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference)
diff --git a/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt
new file mode 100644
index 0000000..744c020
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt
@@ -0,0 +1,47 @@
+package com.android.server.connectivity
+
+import android.content.res.Resources
+import android.net.ConnectivityResources
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.provider.DeviceConfig.OnPropertiesChangedListener
+import com.android.internal.annotations.GuardedBy
+import com.android.server.connectivity.MultinetworkPolicyTracker.CONFIG_ACTIVELY_PREFER_BAD_WIFI
+import java.util.concurrent.Executor
+
+class MultinetworkPolicyTrackerTestDependencies(private val resources: Resources) :
+        MultinetworkPolicyTracker.Dependencies() {
+    @GuardedBy("listeners")
+    private var configActivelyPreferBadWifi = 0
+    // TODO : move this to an actual fake device config object
+    @GuardedBy("listeners")
+    private val listeners = mutableListOf<Pair<Executor, OnPropertiesChangedListener>>()
+
+    fun putConfigActivelyPreferBadWifi(value: Int) {
+        synchronized(listeners) {
+            if (value == configActivelyPreferBadWifi) return
+            configActivelyPreferBadWifi = value
+            val p = DeviceConfig.Properties(NAMESPACE_CONNECTIVITY,
+                    mapOf(CONFIG_ACTIVELY_PREFER_BAD_WIFI to value.toString()))
+            listeners.forEach { (executor, listener) ->
+                executor.execute { listener.onPropertiesChanged(p) }
+            }
+        }
+    }
+
+    override fun getConfigActivelyPreferBadWifi(): Int {
+        return synchronized(listeners) { configActivelyPreferBadWifi }
+    }
+
+    override fun addOnDevicePropertiesChangedListener(
+        e: Executor,
+        listener: OnPropertiesChangedListener
+    ) {
+        synchronized(listeners) {
+            listeners.add(e to listener)
+        }
+    }
+
+    override fun getResourcesForActiveSubId(res: ConnectivityResources, id: Int): Resources =
+            resources
+}
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 53097b6..9a5298d 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -65,9 +65,11 @@
 import androidx.annotation.StringRes;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.UiObject;
 import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
 
 import com.android.connectivity.resources.R;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
@@ -99,6 +101,8 @@
     private static final int TEST_NOTIF_ID = 101;
     private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
     private static final long TEST_TIMEOUT_MS = 10_000L;
+    private static final long UI_AUTOMATOR_WAIT_TIME_MILLIS = TEST_TIMEOUT_MS;
+
     static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -386,8 +390,16 @@
                 R.bool.config_notifyNoInternetAsDialogWhenHighPriority);
 
         final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
+        final UiDevice uiDevice =  UiDevice.getInstance(instr);
         UiDevice.getInstance(instr).pressHome();
 
+        // UiDevice.getLauncherPackageName() requires the test manifest to have a <queries> tag for
+        // the launcher intent.
+        final String launcherPackageName = uiDevice.getLauncherPackageName();
+        assertTrue(String.format("Launcher (%s) is not shown", launcherPackageName),
+                uiDevice.wait(Until.hasObject(By.pkg(launcherPackageName)),
+                        UI_AUTOMATOR_WAIT_TIME_MILLIS));
+
         mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
         // Non-"no internet" notifications are not affected
         verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any());
@@ -407,8 +419,7 @@
         verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId);
 
         // Verify that the activity is shown (the activity shows the action on screen)
-        final UiObject actionText = UiDevice.getInstance(instr).findObject(
-                new UiSelector().text(testAction));
+        final UiObject actionText = uiDevice.findObject(new UiSelector().text(testAction));
         assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS));
 
         // Tapping the text should dismiss the dialog
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
index 6f9f430..1e3f389 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -17,31 +17,38 @@
 package com.android.server.connectivity
 
 import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL as NET_CAP_PORTAL
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkScore.KEEP_CONNECTED_NONE
-import android.net.NetworkScore.POLICY_EXITING
-import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY
-import android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI
+import android.net.NetworkScore.POLICY_EXITING as EXITING
+import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY as PRIMARY
+import android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI as YIELD_TO_BAD_WIFI
 import android.os.Build
 import androidx.test.filters.SmallTest
-import com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD
-import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
+import com.android.connectivity.resources.R
+import com.android.server.connectivity.FullScore.POLICY_AVOIDED_WHEN_UNVALIDATED as AVOIDED_UNVALID
+import com.android.server.connectivity.FullScore.POLICY_EVER_EVALUATED as EVER_EVALUATED
+import com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED as EVER_VALIDATED
+import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED as IS_VALIDATED
 import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 import kotlin.test.assertEquals
 
 private fun score(vararg policies: Int) = FullScore(
         policies.fold(0L) { acc, e -> acc or (1L shl e) }, KEEP_CONNECTED_NONE)
-private fun caps(transport: Int) = NetworkCapabilities.Builder().addTransportType(transport).build()
+private fun caps(transport: Int, vararg capabilities: Int) =
+        NetworkCapabilities.Builder().addTransportType(transport).apply {
+            capabilities.forEach { addCapability(it) }
+        }.build()
 
 @SmallTest
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class NetworkRankerTest {
-    private val mRanker = NetworkRanker()
+@RunWith(Parameterized::class)
+class NetworkRankerTest(private val activelyPreferBadWifi: Boolean) {
+    private val mRanker = NetworkRanker(NetworkRanker.Configuration(activelyPreferBadWifi))
 
     private class TestScore(private val sc: FullScore, private val nc: NetworkCapabilities)
             : NetworkRanker.Scoreable {
@@ -49,124 +56,144 @@
         override fun getCapsNoCopy(): NetworkCapabilities = nc
     }
 
+    @get:Rule
+    val mIgnoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.R)
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun ranker() = listOf(true, false)
+    }
+
+    // Helpers to shorten syntax
+    private fun rank(vararg scores: TestScore) =
+            mRanker.getBestNetworkByPolicy(scores.toList(), null /* currentSatisfier */)
+    val CAPS_CELL = caps(TRANSPORT_CELLULAR)
+    val CAPS_WIFI = caps(TRANSPORT_WIFI)
+    val CAPS_WIFI_PORTAL = caps(TRANSPORT_WIFI, NET_CAP_PORTAL)
+
     @Test
-    fun testYieldToBadWiFiOneCell() {
+    fun testYieldToBadWiFi_oneCell() {
         // Only cell, it wins
-        val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                caps(TRANSPORT_CELLULAR))
-        val scores = listOf(winner)
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(cell, rank(cell))
     }
 
     @Test
-    fun testYieldToBadWiFiOneCellOneBadWiFi() {
+    fun testPreferBadWifi_oneCellOneEvaluatingWifi() {
+        val wifi = TestScore(score(), caps(TRANSPORT_WIFI))
+        val cell = TestScore(score(YIELD_TO_BAD_WIFI, IS_VALIDATED, EVER_EVALUATED), CAPS_CELL)
+        assertEquals(cell, rank(wifi, cell))
+    }
+
+    @Test
+    fun testYieldToBadWiFi_oneCellOneBadWiFi() {
         // Bad wifi wins against yielding validated cell
-        val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
-                caps(TRANSPORT_WIFI))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val badWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED), CAPS_WIFI)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(badWifi, rank(badWifi, cell))
     }
 
     @Test
-    fun testYieldToBadWiFiOneCellTwoBadWiFi() {
+    fun testPreferBadWifi_oneCellOneBadWifi() {
+        val badWifi = TestScore(score(EVER_EVALUATED), CAPS_WIFI)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        val winner = if (activelyPreferBadWifi) badWifi else cell
+        assertEquals(winner, rank(badWifi, cell))
+    }
+
+    @Test
+    fun testPreferBadWifi_oneCellOneCaptivePortalWifi() {
+        val portalWifi = TestScore(score(EVER_EVALUATED), CAPS_WIFI_PORTAL)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(cell, rank(portalWifi, cell))
+    }
+
+    @Test
+    fun testYieldToBadWifi_oneCellOneCaptivePortalWifiThatClosed() {
+        val portalWifiClosed = TestScore(score(EVER_EVALUATED, EVER_VALIDATED), CAPS_WIFI_PORTAL)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(portalWifiClosed, rank(portalWifiClosed, cell))
+    }
+
+    @Test
+    fun testYieldToBadWifi_avoidUnvalidated() {
+        // Bad wifi avoided when unvalidated loses against yielding validated cell
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        val avoidedWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, AVOIDED_UNVALID),
+                CAPS_WIFI)
+        assertEquals(cell, rank(cell, avoidedWifi))
+    }
+
+    @Test
+    fun testYieldToBadWiFi_oneCellTwoBadWiFi() {
         // Bad wifi wins against yielding validated cell. Prefer the one that's primary.
-        val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
-                        caps(TRANSPORT_WIFI)),
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val primaryBadWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, PRIMARY), CAPS_WIFI)
+        val secondaryBadWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED), CAPS_WIFI)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(primaryBadWifi, rank(primaryBadWifi, secondaryBadWifi, cell))
     }
 
     @Test
-    fun testYieldToBadWiFiOneCellTwoBadWiFiOneNotAvoided() {
+    fun testYieldToBadWiFi_oneCellTwoBadWiFiOneNotAvoided() {
         // Bad wifi ever validated wins against bad wifi that never was validated (or was
         // avoided when bad).
-        val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
-                caps(TRANSPORT_WIFI))
-        val scores = listOf(
-                winner,
-                TestScore(score(), caps(TRANSPORT_WIFI)),
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val badWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED), CAPS_WIFI)
+        val neverValidatedWifi = TestScore(score(), CAPS_WIFI)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(badWifi, rank(badWifi, neverValidatedWifi, cell))
     }
 
     @Test
-    fun testYieldToBadWiFiOneCellOneBadWiFiOneGoodWiFi() {
+    fun testYieldToBadWiFi_oneCellOneBadWiFiOneGoodWiFi() {
         // Good wifi wins
-        val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                        POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val goodWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, IS_VALIDATED), CAPS_WIFI)
+        val badWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, PRIMARY), CAPS_WIFI)
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(goodWifi, rank(goodWifi, badWifi, cell))
     }
 
     @Test
-    fun testYieldToBadWiFiTwoCellsOneBadWiFi() {
+    fun testPreferBadWifi_oneCellOneBadWifiOneEvaluatingWifi() {
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        val badWifi = TestScore(score(EVER_EVALUATED), CAPS_WIFI)
+        val evaluatingWifi = TestScore(score(), CAPS_WIFI)
+        val winner = if (activelyPreferBadWifi) badWifi else cell
+        assertEquals(winner, rank(cell, badWifi, evaluatingWifi))
+    }
+
+    @Test
+    fun testYieldToBadWiFi_twoCellsOneBadWiFi() {
         // Cell that doesn't yield wins over cell that yields and bad wifi
-        val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                        POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val cellNotYield = TestScore(score(EVER_EVALUATED, IS_VALIDATED), CAPS_CELL)
+        val badWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, PRIMARY), CAPS_WIFI)
+        val cellYield = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(cellNotYield, rank(cellNotYield, badWifi, cellYield))
     }
 
     @Test
-    fun testYieldToBadWiFiTwoCellsOneBadWiFiOneGoodWiFi() {
+    fun testYieldToBadWiFi_twoCellsOneBadWiFiOneGoodWiFi() {
         // Good wifi wins over cell that doesn't yield and cell that yields
-        val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                        POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
-                TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR)),
-                TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                        caps(TRANSPORT_CELLULAR))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val goodWifi = TestScore(score(EVER_EVALUATED, IS_VALIDATED), CAPS_WIFI)
+        val badWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, PRIMARY), CAPS_WIFI)
+        val cellNotYield = TestScore(score(EVER_EVALUATED, IS_VALIDATED), CAPS_CELL)
+        val cellYield = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        assertEquals(goodWifi, rank(goodWifi, badWifi, cellNotYield, cellYield))
     }
 
     @Test
-    fun testYieldToBadWiFiOneExitingGoodWiFi() {
+    fun testYieldToBadWiFi_oneExitingGoodWiFi() {
         // Yielding cell wins over good exiting wifi
-        val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                caps(TRANSPORT_CELLULAR))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_IS_VALIDATED, POLICY_EXITING), caps(TRANSPORT_WIFI))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        val exitingWifi = TestScore(score(EVER_EVALUATED, IS_VALIDATED, EXITING), CAPS_WIFI)
+        assertEquals(cell, rank(cell, exitingWifi))
     }
 
     @Test
-    fun testYieldToBadWiFiOneExitingBadWiFi() {
+    fun testYieldToBadWiFi_oneExitingBadWiFi() {
         // Yielding cell wins over bad exiting wifi
-        val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
-                caps(TRANSPORT_CELLULAR))
-        val scores = listOf(
-                winner,
-                TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
-                        POLICY_EXITING), caps(TRANSPORT_WIFI))
-        )
-        assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+        val cell = TestScore(score(EVER_EVALUATED, YIELD_TO_BAD_WIFI, IS_VALIDATED), CAPS_CELL)
+        val badExitingWifi = TestScore(score(EVER_EVALUATED, EVER_VALIDATED, EXITING), CAPS_WIFI)
+        assertEquals(cell, rank(cell, badExitingWifi))
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 354e79a..8076edb 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -77,6 +77,8 @@
 import android.net.UidRange;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Process;
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
@@ -96,6 +98,7 @@
 import com.android.server.BpfNetMaps;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -153,6 +156,7 @@
     private static final int VERSION_Q = Build.VERSION_CODES.Q;
     private static final int PERMISSION_TRAFFIC_ALL =
             PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS;
+    private static final int TIMEOUT_MS = 2_000;
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
@@ -165,7 +169,7 @@
     private PermissionMonitor mPermissionMonitor;
     private NetdMonitor mNetdMonitor;
     private BpfMapMonitor mBpfMapMonitor;
-
+    private HandlerThread mHandlerThread;
     private ProcessShim mProcessShim = ProcessShimImpl.newInstance();
 
     @Before
@@ -195,12 +199,17 @@
         // by default.
         doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
 
-        mPermissionMonitor = new PermissionMonitor(mContext, mNetdService, mBpfNetMaps, mDeps);
+        mHandlerThread = new HandlerThread("PermissionMonitorTest");
+        mPermissionMonitor = new PermissionMonitor(
+                mContext, mNetdService, mBpfNetMaps, mDeps, mHandlerThread);
         mNetdMonitor = new NetdMonitor(mNetdService);
         mBpfMapMonitor = new BpfMapMonitor(mBpfNetMaps);
 
+        // Start the HandlerThread after PermissionMonitor created as CS current behavior.
+        mHandlerThread.start();
+
         doReturn(List.of()).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
-        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        onUserAdded(MOCK_USER1);
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion,
@@ -288,9 +297,39 @@
         doReturn(newPackages).when(mPackageManager).getPackagesForUid(eq(uid));
     }
 
+    private void startMonitoring() {
+        processOnHandlerThread(() -> mPermissionMonitor.startMonitoring());
+    }
+
+    private void onUserAdded(UserHandle user) {
+        processOnHandlerThread(() -> mPermissionMonitor.onUserAdded(user));
+    }
+
+    private void onUserRemoved(UserHandle user) {
+        processOnHandlerThread(() -> mPermissionMonitor.onUserRemoved(user));
+    }
+
+    private void onPackageAdded(String packageName, int uid) {
+        processOnHandlerThread(() -> mPermissionMonitor.onPackageAdded(packageName, uid));
+    }
+
+    private void onPackageRemoved(String packageName, int uid) {
+        processOnHandlerThread(() -> mPermissionMonitor.onPackageRemoved(packageName, uid));
+    }
+
+    private void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
+        processOnHandlerThread(() ->
+                mPermissionMonitor.sendAppIdsTrafficPermission(netdPermissionsAppIds));
+    }
+
+    private void sendPackagePermissionsForAppId(int appId, int permissions) {
+        processOnHandlerThread(() ->
+                mPermissionMonitor.sendPackagePermissionsForAppId(appId, permissions));
+    }
+
     private void addPackage(String packageName, int uid, String... permissions) throws Exception {
         buildAndMockPackageInfoWithPermissions(packageName, uid, permissions);
-        mPermissionMonitor.onPackageAdded(packageName, uid);
+        processOnHandlerThread(() -> mPermissionMonitor.onPackageAdded(packageName, uid));
     }
 
     private void removePackage(String packageName, int uid) {
@@ -302,7 +341,7 @@
         final String[] newPackages = Arrays.stream(oldPackages).filter(e -> !e.equals(packageName))
                 .toArray(String[]::new);
         doReturn(newPackages).when(mPackageManager).getPackagesForUid(eq(uid));
-        mPermissionMonitor.onPackageRemoved(packageName, uid);
+        processOnHandlerThread(() -> mPermissionMonitor.onPackageRemoved(packageName, uid));
     }
 
     @Test
@@ -684,7 +723,7 @@
                 CHANGE_NETWORK_STATE);
 
         // Add user MOCK_USER1.
-        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        onUserAdded(MOCK_USER1);
         // Add SYSTEM_PACKAGE2, expect only have network permission.
         addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_APPID1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
@@ -702,7 +741,7 @@
         doReturn(pkgs).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS),
                 eq(MOCK_USER_ID2));
         // Add user MOCK_USER2.
-        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        onUserAdded(MOCK_USER2);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_APPID1);
 
@@ -743,7 +782,7 @@
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
                 SYSTEM_APPID1);
 
-        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        onUserRemoved(MOCK_USER1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER2},
                 SYSTEM_APPID1);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, SYSTEM_APPID1);
@@ -757,7 +796,7 @@
                 MOCK_APPID1);
 
         // Remove last user, expect no permission change.
-        mPermissionMonitor.onUserRemoved(MOCK_USER2);
+        onUserRemoved(MOCK_USER2);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1,
                 MOCK_APPID1);
     }
@@ -772,7 +811,7 @@
                 buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
         buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         // Every app on user 0 except MOCK_UID12 is subject to the VPN.
         final Set<UidRange> vpnRange1 = Set.of(
                 new UidRange(0, MOCK_UID12 - 1),
@@ -786,9 +825,9 @@
         reset(mBpfNetMaps);
 
         // When MOCK_UID11 package is uninstalled and reinstalled, expect Netd to be updated
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         verify(mBpfNetMaps).removeUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
-        mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageAdded(MOCK_PACKAGE1, MOCK_UID11);
         verify(mBpfNetMaps).addUidInterfaceRules(eq(ifName), aryEq(new int[]{MOCK_UID11}));
 
         reset(mBpfNetMaps);
@@ -829,7 +868,7 @@
         buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
         doReturn(List.of(MOCK_USER1, MOCK_USER2)).when(mUserManager).getUserHandles(eq(true));
 
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1),
                 UidRange.createForUser(MOCK_USER2));
         mPermissionMonitor.onVpnUidRangesAdded(ifName, vpnRange, VPN_UID);
@@ -840,7 +879,7 @@
         verify(mBpfNetMaps).addUidInterfaceRules(eq(ifName), aryEq(new int[]{MOCK_UID21}));
 
         // Removed package should have its uid rules removed
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         verify(mBpfNetMaps).removeUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
         verify(mBpfNetMaps, never()).removeUidInterfaceRules(aryEq(new int[]{MOCK_UID21}));
     }
@@ -864,7 +903,7 @@
                 buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12),
                 buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         // Every app on user 0 except MOCK_UID12 is subject to the VPN.
         final UidRange[] lockdownRange = {
                 new UidRange(0, MOCK_UID12 - 1),
@@ -896,7 +935,7 @@
                 buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
                 buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         // MOCK_UID11 is subject to the VPN.
         final UidRange range = new UidRange(MOCK_UID11, MOCK_UID11);
         final UidRange[] lockdownRange = {range};
@@ -941,7 +980,7 @@
                 buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
                 buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         // MOCK_UID11 is subject to the VPN.
         final UidRange range = new UidRange(MOCK_UID11, MOCK_UID11);
         final UidRange[] lockdownRangeDuplicates = {range, range};
@@ -979,7 +1018,7 @@
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
         doReturn(List.of(MOCK_USER1, MOCK_USER2)).when(mUserManager).getUserHandles(eq(true));
 
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final UidRange[] lockdownRange = {
                 UidRange.createForUser(MOCK_USER1),
                 UidRange.createForUser(MOCK_USER2)
@@ -997,7 +1036,7 @@
         reset(mBpfNetMaps);
 
         // Uninstalling package should remove Lockdown rules
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(false) /* add */);
         verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, false /* add */);
     }
@@ -1007,13 +1046,15 @@
     // called multiple times with the uid corresponding to each user.
     private void addPackageForUsers(UserHandle[] users, String packageName, int appId) {
         for (final UserHandle user : users) {
-            mPermissionMonitor.onPackageAdded(packageName, user.getUid(appId));
+            processOnHandlerThread(() ->
+                    mPermissionMonitor.onPackageAdded(packageName, user.getUid(appId)));
         }
     }
 
     private void removePackageForUsers(UserHandle[] users, String packageName, int appId) {
         for (final UserHandle user : users) {
-            mPermissionMonitor.onPackageRemoved(packageName, user.getUid(appId));
+            processOnHandlerThread(() ->
+                    mPermissionMonitor.onPackageRemoved(packageName, user.getUid(appId)));
         }
     }
 
@@ -1039,7 +1080,7 @@
         netdPermissionsAppIds.put(SYSTEM_APPID2, PERMISSION_UPDATE_DEVICE_STATS);
 
         // Send the permission information to netd, expect permission updated.
-        mPermissionMonitor.sendAppIdsTrafficPermission(netdPermissionsAppIds);
+        sendAppIdsTrafficPermission(netdPermissionsAppIds);
 
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID2);
@@ -1047,16 +1088,16 @@
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, SYSTEM_APPID2);
 
         // Update permission of MOCK_APPID1, expect new permission show up.
-        mPermissionMonitor.sendPackagePermissionsForAppId(MOCK_APPID1, PERMISSION_TRAFFIC_ALL);
+        sendPackagePermissionsForAppId(MOCK_APPID1, PERMISSION_TRAFFIC_ALL);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         // Change permissions of SYSTEM_APPID2, expect new permission show up and old permission
         // revoked.
-        mPermissionMonitor.sendPackagePermissionsForAppId(SYSTEM_APPID2, PERMISSION_INTERNET);
+        sendPackagePermissionsForAppId(SYSTEM_APPID2, PERMISSION_INTERNET);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, SYSTEM_APPID2);
 
         // Revoke permission from SYSTEM_APPID1, expect no permission stored.
-        mPermissionMonitor.sendPackagePermissionsForAppId(SYSTEM_APPID1, PERMISSION_NONE);
+        sendPackagePermissionsForAppId(SYSTEM_APPID1, PERMISSION_NONE);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_NONE, SYSTEM_APPID1);
     }
 
@@ -1086,7 +1127,7 @@
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
     }
 
@@ -1096,7 +1137,7 @@
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
 
         when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
@@ -1124,7 +1165,7 @@
         // Uninstall MOCK_PACKAGE1 and expect only INTERNET permission left.
         when(mPackageManager.getPackagesForUid(eq(MOCK_UID11)))
                 .thenReturn(new String[]{MOCK_PACKAGE2});
-        mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+        onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
     }
 
@@ -1133,8 +1174,8 @@
         // Use the real context as this test must ensure the *real* system package holds the
         // necessary permission.
         final Context realContext = InstrumentationRegistry.getContext();
-        final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService,
-                mBpfNetMaps);
+        final PermissionMonitor monitor = new PermissionMonitor(
+                realContext, mNetdService, mBpfNetMaps, mHandlerThread);
         final PackageManager manager = realContext.getPackageManager();
         final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME,
                 GET_PERMISSIONS | MATCH_ANY_USER);
@@ -1148,7 +1189,7 @@
         when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS)))
                 .thenReturn(new int[]{ MOCK_UID12 });
 
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID2);
     }
@@ -1165,12 +1206,24 @@
                     }
                     return true;
                 }), any(), any());
-        return receiverCaptor.getValue();
+        final BroadcastReceiver originalReceiver = receiverCaptor.getValue();
+        return new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                processOnHandlerThread(() -> originalReceiver.onReceive(context, intent));
+            }
+        };
+    }
+
+    private void processOnHandlerThread(Runnable function) {
+        final Handler handler = mHandlerThread.getThreadHandler();
+        handler.post(() -> function.run());
+        HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
     }
 
     @Test
     public void testIntentReceiver() throws Exception {
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final BroadcastReceiver receiver = expectBroadcastReceiver(
                 Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED);
 
@@ -1197,12 +1250,18 @@
                 ArgumentCaptor.forClass(ContentObserver.class);
         verify(mDeps).registerContentObserver(any(),
                 argThat(uri -> uri.equals(expectedUri)), anyBoolean(), captor.capture());
-        return captor.getValue();
+        final ContentObserver originalObserver = captor.getValue();
+        return new ContentObserver(null) {
+            @Override
+            public void onChange(final boolean selfChange) {
+                processOnHandlerThread(() -> originalObserver.onChange(selfChange));
+            }
+        };
     }
 
     @Test
     public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception {
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final ContentObserver contentObserver = expectRegisterContentObserver(
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
@@ -1234,7 +1293,7 @@
 
     @Test
     public void testUidsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception {
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final ContentObserver contentObserver = expectRegisterContentObserver(
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
@@ -1267,7 +1326,7 @@
 
     @Test
     public void testUidsAllowedOnRestrictedNetworksChangedWithMultipleUsers() throws Exception {
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final ContentObserver contentObserver = expectRegisterContentObserver(
                 Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
 
@@ -1288,7 +1347,7 @@
         buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID22);
         doReturn(pkgs).when(mPackageManager)
                 .getInstalledPackagesAsUser(eq(GET_PERMISSIONS), eq(MOCK_USER_ID2));
-        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        onUserAdded(MOCK_USER2);
         // MOCK_APPID1 in MOCK_USER1 should have SYSTEM permission but in MOCK_USER2 should have no
         // permissions. And MOCK_APPID2 has no permissions in either users.
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
@@ -1307,7 +1366,7 @@
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
 
         // Remove user MOCK_USER1
-        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        onUserRemoved(MOCK_USER1);
         mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2},
                 MOCK_APPID2);
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1);
@@ -1327,7 +1386,7 @@
                 buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
                 buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1, MOCK_APPID2);
 
@@ -1353,7 +1412,7 @@
     @Test
     public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring()
             throws Exception {
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         final BroadcastReceiver receiver = expectBroadcastReceiver(
                 Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
 
@@ -1386,7 +1445,7 @@
                 buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
                 buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
 
@@ -1413,7 +1472,7 @@
                 buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
                 buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE, INTERNET)))
                 .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
-        mPermissionMonitor.startMonitoring();
+        startMonitoring();
         mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
                 MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
@@ -1478,7 +1537,7 @@
 
     private void addUserAndVerifyAppIdsPermissions(UserHandle user, int appId1Perm,
             int appId2Perm, int appId3Perm) {
-        mPermissionMonitor.onUserAdded(user);
+        processOnHandlerThread(() -> mPermissionMonitor.onUserAdded(user));
         mBpfMapMonitor.expectTrafficPerm(appId1Perm, MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(appId2Perm, MOCK_APPID2);
         mBpfMapMonitor.expectTrafficPerm(appId3Perm, MOCK_APPID3);
@@ -1486,7 +1545,7 @@
 
     private void removeUserAndVerifyAppIdsPermissions(UserHandle user, int appId1Perm,
             int appId2Perm, int appId3Perm) {
-        mPermissionMonitor.onUserRemoved(user);
+        processOnHandlerThread(() -> mPermissionMonitor.onUserRemoved(user));
         mBpfMapMonitor.expectTrafficPerm(appId1Perm, MOCK_APPID1);
         mBpfMapMonitor.expectTrafficPerm(appId2Perm, MOCK_APPID2);
         mBpfMapMonitor.expectTrafficPerm(appId3Perm, MOCK_APPID3);
@@ -1528,8 +1587,8 @@
     @Test
     public void testAppIdsTrafficPermission_Multiuser_PackageAdded() throws Exception {
         // Add two users with empty package list.
-        mPermissionMonitor.onUserAdded(MOCK_USER1);
-        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        onUserAdded(MOCK_USER1);
+        onUserAdded(MOCK_USER2);
 
         final int[] netdPermissions = {PERMISSION_NONE, PERMISSION_INTERNET,
                 PERMISSION_UPDATE_DEVICE_STATS, PERMISSION_TRAFFIC_ALL};
@@ -1598,8 +1657,8 @@
     @Test
     public void testAppIdsTrafficPermission_Multiuser_PackageRemoved() throws Exception {
         // Add two users with empty package list.
-        mPermissionMonitor.onUserAdded(MOCK_USER1);
-        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        onUserAdded(MOCK_USER1);
+        onUserAdded(MOCK_USER2);
 
         int appId = MOCK_APPID1;
         // Verify that the permission combination is expected when same appId package is removed on
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 1fda04e..677e7b6 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -20,6 +20,8 @@
 import static android.Manifest.permission.CONTROL_VPN;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import static android.net.ConnectivityManager.NetworkCallback;
 import static android.net.INetd.IF_STATE_DOWN;
 import static android.net.INetd.IF_STATE_UP;
@@ -29,7 +31,7 @@
 import static android.os.Build.VERSION_CODES.S_V2;
 import static android.os.UserHandle.PER_USER_RANGE;
 
-import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
 import static com.android.testutils.Cleanup.testAndCleanup;
 import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import static com.android.testutils.MiscAsserts.assertThrows;
@@ -41,7 +43,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -78,6 +79,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.net.ConnectivityDiagnosticsManager;
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.Ikev2VpnProfile;
@@ -92,6 +94,7 @@
 import android.net.LinkProperties;
 import android.net.LocalSocket;
 import android.net.Network;
+import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
@@ -117,6 +120,7 @@
 import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.PowerWhitelistManager;
 import android.os.Process;
 import android.os.UserHandle;
@@ -136,7 +140,6 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.util.HexDump;
-import com.android.modules.utils.build.SdkLevel;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.IpSecService;
 import com.android.server.VpnTestBase;
@@ -203,6 +206,7 @@
     private static final byte[] TEST_VPN_PSK = "psk".getBytes();
 
     private static final int IP4_PREFIX_LEN = 32;
+    private static final int IP6_PREFIX_LEN = 64;
     private static final int MIN_PORT = 0;
     private static final int MAX_PORT = 65535;
 
@@ -216,15 +220,28 @@
             InetAddresses.parseNumericAddress("192.0.2.201");
     private static final InetAddress TEST_VPN_INTERNAL_IP =
             InetAddresses.parseNumericAddress("198.51.100.10");
+    private static final InetAddress TEST_VPN_INTERNAL_IP6 =
+            InetAddresses.parseNumericAddress("2001:db8::1");
     private static final InetAddress TEST_VPN_INTERNAL_DNS =
             InetAddresses.parseNumericAddress("8.8.8.8");
+    private static final InetAddress TEST_VPN_INTERNAL_DNS6 =
+            InetAddresses.parseNumericAddress("2001:4860:4860::8888");
 
     private static final IkeTrafficSelector IN_TS =
             new IkeTrafficSelector(MIN_PORT, MAX_PORT, TEST_VPN_INTERNAL_IP, TEST_VPN_INTERNAL_IP);
+    private static final IkeTrafficSelector IN_TS6 =
+            new IkeTrafficSelector(
+                    MIN_PORT, MAX_PORT, TEST_VPN_INTERNAL_IP6, TEST_VPN_INTERNAL_IP6);
     private static final IkeTrafficSelector OUT_TS =
             new IkeTrafficSelector(MIN_PORT, MAX_PORT,
                     InetAddresses.parseNumericAddress("0.0.0.0"),
                     InetAddresses.parseNumericAddress("255.255.255.255"));
+    private static final IkeTrafficSelector OUT_TS6 =
+            new IkeTrafficSelector(
+                    MIN_PORT,
+                    MAX_PORT,
+                    InetAddresses.parseNumericAddress("::"),
+                    InetAddresses.parseNumericAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
 
     private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE);
     private static final Network TEST_NETWORK_2 = new Network(Integer.MAX_VALUE - 1);
@@ -248,6 +265,7 @@
     @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
     @Mock private Vpn.VpnNetworkAgentWrapper mMockNetworkAgent;
     @Mock private ConnectivityManager mConnectivityManager;
+    @Mock private ConnectivityDiagnosticsManager mCdm;
     @Mock private IpSecService mIpSecService;
     @Mock private VpnProfileStore mVpnProfileStore;
     @Mock private ScheduledThreadPoolExecutor mExecutor;
@@ -275,6 +293,10 @@
 
         mIpSecManager = new IpSecManager(mContext, mIpSecService);
         mTestDeps = spy(new TestDeps());
+        doReturn(IPV6_MIN_MTU)
+                .when(mTestDeps)
+                .calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+        doReturn(1500).when(mTestDeps).getJavaNetworkInterfaceMtu(any(), anyInt());
 
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         setMockedPackages(sPackages);
@@ -286,6 +308,8 @@
         mockService(NotificationManager.class, Context.NOTIFICATION_SERVICE, mNotificationManager);
         mockService(ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mConnectivityManager);
         mockService(IpSecManager.class, Context.IPSEC_SERVICE, mIpSecManager);
+        mockService(ConnectivityDiagnosticsManager.class, Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+                mCdm);
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -311,6 +335,8 @@
                         IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME);
         when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any()))
                 .thenReturn(tunnelResp);
+        doReturn(new LinkProperties()).when(mConnectivityManager).getLinkProperties(any());
+
         // The unit test should know what kind of permission it needs and set the permission by
         // itself, so set the default value of Context#checkCallingOrSelfPermission to
         // PERMISSION_DENIED.
@@ -341,7 +367,7 @@
 
     @After
     public void tearDown() throws Exception {
-        doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+        doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
     }
 
     private <T> void mockService(Class<T> clazz, String name, T service) {
@@ -695,7 +721,6 @@
     @Test
     public void testPrepare_throwSecurityExceptionWhenGivenPackageDoesNotBelongToTheCaller()
             throws Exception {
-        assumeTrue(isAtLeastT());
         final Vpn vpn = createVpnAndSetupUidChecks();
         assertThrows(SecurityException.class,
                 () -> vpn.prepare("com.not.vpn.owner", null, VpnManager.TYPE_VPN_SERVICE));
@@ -944,12 +969,15 @@
     @Test
     public void testSetAndGetAppExclusionListRestrictedUser() throws Exception {
         final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+
         // Mock it to restricted profile
         when(mUserManager.getUserInfo(anyInt())).thenReturn(RESTRICTED_PROFILE_A);
+
         // Restricted users cannot configure VPNs
         assertThrows(SecurityException.class,
                 () -> vpn.setAppExclusionList(TEST_VPN_PKG, new ArrayList<>()));
-        assertThrows(SecurityException.class, () -> vpn.getAppExclusionList(TEST_VPN_PKG));
+
+        assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
     }
 
     @Test
@@ -1165,7 +1193,6 @@
 
     @Test
     public void testStartOpAndFinishOpWillBeCalledWhenPlatformVpnIsOnAndOff() throws Exception {
-        assumeTrue(SdkLevel.isAtLeastT());
         final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
         when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
                 .thenReturn(mVpnProfile.encode());
@@ -1191,7 +1218,6 @@
 
     @Test
     public void testStartOpWithSeamlessHandover() throws Exception {
-        assumeTrue(SdkLevel.isAtLeastT());
         final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN);
         assertTrue(vpn.prepare(TEST_VPN_PKG, null, VpnManager.TYPE_VPN_SERVICE));
         final VpnConfig config = new VpnConfig();
@@ -1223,7 +1249,7 @@
     }
 
     private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass,
-            int errorCode, VpnProfileState... profileState) {
+            int errorCode, String[] packageName, VpnProfileState... profileState) {
         final Context userContext =
                 mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
         final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -1233,13 +1259,32 @@
 
         for (int i = 0; i < verifyTimes; i++) {
             final Intent intent = intentArgumentCaptor.getAllValues().get(i);
+            assertEquals(packageName[i], intent.getPackage());
             assertEquals(sessionKey, intent.getStringExtra(VpnManager.EXTRA_SESSION_KEY));
             final Set<String> categories = intent.getCategories();
             assertTrue(categories.contains(category));
+            assertEquals(1, categories.size());
             assertEquals(errorClass,
                     intent.getIntExtra(VpnManager.EXTRA_ERROR_CLASS, -1 /* defaultValue */));
             assertEquals(errorCode,
                     intent.getIntExtra(VpnManager.EXTRA_ERROR_CODE, -1 /* defaultValue */));
+            // CATEGORY_EVENT_DEACTIVATED_BY_USER & CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED won't
+            // send NetworkCapabilities & LinkProperties to VPN app.
+            // For ERROR_CODE_NETWORK_LOST, the NetworkCapabilities & LinkProperties of underlying
+            // network will be cleared. So the VPN app will receive null for those 2 extra values.
+            if (category.equals(VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER)
+                    || category.equals(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED)
+                    || errorCode == VpnManager.ERROR_CODE_NETWORK_LOST) {
+                assertNull(intent.getParcelableExtra(
+                        VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES));
+                assertNull(intent.getParcelableExtra(VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
+            } else {
+                assertNotNull(intent.getParcelableExtra(
+                        VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES));
+                assertNotNull(intent.getParcelableExtra(
+                        VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
+            }
+
             if (profileState != null) {
                 assertEquals(profileState[i], intent.getParcelableExtra(
                         VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
@@ -1248,9 +1293,21 @@
         reset(userContext);
     }
 
+    private void verifyDeactivatedByUser(String sessionKey, String[] packageName) {
+        // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
+        // errorCode won't be set.
+        verifyVpnManagerEvent(sessionKey, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
+                -1 /* errorClass */, -1 /* errorCode */, packageName, null /* profileState */);
+    }
+
+    private void verifyAlwaysOnStateChanged(String[] packageName, VpnProfileState... profileState) {
+        verifyVpnManagerEvent(null /* sessionKey */,
+                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+                -1 /* errorCode */, packageName, profileState);
+    }
+
     @Test
     public void testVpnManagerEventForUserDeactivated() throws Exception {
-        assumeTrue(SdkLevel.isAtLeastT());
         // For security reasons, Vpn#prepare() will check that oldPackage and newPackage are either
         // null or the package of the caller. This test will call Vpn#prepare() to pretend the old
         // VPN is replaced by a new one. But only Settings can change to some other packages, and
@@ -1268,10 +1325,7 @@
         verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
         verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
         reset(mDeviceIdleInternal);
-        // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
-        // errorCode won't be set.
-        verifyVpnManagerEvent(sessionKey1, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-                -1 /* errorClass */, -1 /* errorCode */, null /* profileState */);
+        verifyDeactivatedByUser(sessionKey1, new String[] {TEST_VPN_PKG});
         reset(mAppOps);
 
         // Test the case that the user chooses another vpn and the original one is replaced.
@@ -1281,15 +1335,11 @@
         verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
         verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
         reset(mDeviceIdleInternal);
-        // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
-        // errorCode won't be set.
-        verifyVpnManagerEvent(sessionKey2, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-                -1 /* errorClass */, -1 /* errorCode */, null /* profileState */);
+        verifyDeactivatedByUser(sessionKey2, new String[] {TEST_VPN_PKG});
     }
 
     @Test
     public void testVpnManagerEventForAlwaysOnChanged() throws Exception {
-        assumeTrue(SdkLevel.isAtLeastT());
         // Calling setAlwaysOnPackage() needs to hold CONTROL_VPN.
         doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
         final Vpn vpn = createVpn(PRIMARY_USER.id);
@@ -1298,9 +1348,8 @@
                 null /* lockdownAllowlist */));
         verifyPowerSaveTempWhitelistApp(PKGS[1]);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
 
         // Enable VPN lockdown for PKGS[1].
@@ -1308,9 +1357,8 @@
                 null /* lockdownAllowlist */));
         verifyPowerSaveTempWhitelistApp(PKGS[1]);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, true /* alwaysOn */, true /* lockdown */));
 
         // Disable VPN lockdown for PKGS[1].
@@ -1318,9 +1366,8 @@
                 null /* lockdownAllowlist */));
         verifyPowerSaveTempWhitelistApp(PKGS[1]);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
 
         // Disable VPN always-on.
@@ -1328,9 +1375,8 @@
                 null /* lockdownAllowlist */));
         verifyPowerSaveTempWhitelistApp(PKGS[1]);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, false /* alwaysOn */, false /* lockdown */));
 
         // Enable VPN always-on for PKGS[1] again.
@@ -1338,9 +1384,8 @@
                 null /* lockdownAllowlist */));
         verifyPowerSaveTempWhitelistApp(PKGS[1]);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
 
         // Enable VPN always-on for PKGS[2].
@@ -1352,9 +1397,8 @@
         // Pass 2 VpnProfileState objects to verifyVpnManagerEvent(), the first one is sent to
         // PKGS[1] to notify PKGS[1] that the VPN always-on is disabled, the second one is sent to
         // PKGS[2] to notify PKGS[2] that the VPN always-on is enabled.
-        verifyVpnManagerEvent(null /* sessionKey */,
-                VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
-                -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+        verifyAlwaysOnStateChanged(new String[] {PKGS[1], PKGS[2]},
+                new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, false /* alwaysOn */, false /* lockdown */),
                 new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
                         null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
@@ -1434,7 +1478,7 @@
         final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
                 ArgumentCaptor.forClass(NetworkCallback.class);
         verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
-                .requestNetwork(any(), networkCallbackCaptor.capture());
+                .registerSystemDefaultNetworkCallback(networkCallbackCaptor.capture(), any());
 
         // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be
         // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException.
@@ -1443,6 +1487,12 @@
         when(mNetd.interfaceGetCfg(anyString())).thenReturn(config);
         final NetworkCallback cb = networkCallbackCaptor.getValue();
         cb.onAvailable(TEST_NETWORK);
+        // Trigger onCapabilitiesChanged() and onLinkPropertiesChanged() so the test can verify that
+        // if NetworkCapabilities and LinkProperties of underlying network will be sent/cleared or
+        // not.
+        // See verifyVpnManagerEvent().
+        cb.onCapabilitiesChanged(TEST_NETWORK, new NetworkCapabilities());
+        cb.onLinkPropertiesChanged(TEST_NETWORK, new LinkProperties());
         return cb;
     }
 
@@ -1461,6 +1511,11 @@
         when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
                 .thenReturn(mVpnProfile.encode());
 
+        doReturn(new NetworkCapabilities()).when(mConnectivityManager)
+                .getRedactedNetworkCapabilitiesForPackage(any(), anyInt(), anyString());
+        doReturn(new LinkProperties()).when(mConnectivityManager)
+                .getRedactedLinkPropertiesForPackage(any(), anyInt(), anyString());
+
         final String sessionKey = vpn.startVpnProfile(TEST_VPN_PKG);
         final NetworkCallback cb = triggerOnAvailableAndGetCallback();
 
@@ -1471,16 +1526,29 @@
         verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
                 .createIkeSession(any(), any(), any(), any(), captor.capture(), any());
         reset(mIkev2SessionCreator);
-        final IkeSessionCallback ikeCb = captor.getValue();
-        ikeCb.onClosedWithException(exception);
+        // For network lost case, the process should be triggered by calling onLost(), which is the
+        // same process with the real case.
+        if (errorCode == VpnManager.ERROR_CODE_NETWORK_LOST) {
+            cb.onLost(TEST_NETWORK);
+            final ArgumentCaptor<Runnable> runnableCaptor =
+                    ArgumentCaptor.forClass(Runnable.class);
+            verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
+            runnableCaptor.getValue().run();
+        } else {
+            final IkeSessionCallback ikeCb = captor.getValue();
+            ikeCb.onClosedWithException(exception);
+        }
 
         verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
         reset(mDeviceIdleInternal);
-        verifyVpnManagerEvent(sessionKey, category, errorType, errorCode, null /* profileState */);
+        verifyVpnManagerEvent(sessionKey, category, errorType, errorCode,
+                new String[] {TEST_VPN_PKG}, null /* profileState */);
         if (errorType == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
             verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
                     .unregisterNetworkCallback(eq(cb));
-        } else if (errorType == VpnManager.ERROR_CLASS_RECOVERABLE) {
+        } else if (errorType == VpnManager.ERROR_CLASS_RECOVERABLE
+                // Vpn won't retry when there is no usable underlying network.
+                && errorCode != VpnManager.ERROR_CODE_NETWORK_LOST) {
             int retryIndex = 0;
             final IkeSessionCallback ikeCb2 = verifyRetryAndGetNewIkeCb(retryIndex++);
 
@@ -1687,9 +1755,12 @@
     }
 
     private ChildSessionConfiguration createChildConfig() {
-        return new ChildSessionConfiguration.Builder(Arrays.asList(IN_TS), Arrays.asList(OUT_TS))
+        return new ChildSessionConfiguration.Builder(
+                        Arrays.asList(IN_TS, IN_TS6), Arrays.asList(OUT_TS, OUT_TS6))
                 .addInternalAddress(new LinkAddress(TEST_VPN_INTERNAL_IP, IP4_PREFIX_LEN))
+                .addInternalAddress(new LinkAddress(TEST_VPN_INTERNAL_IP6, IP6_PREFIX_LEN))
                 .addInternalDnsServer(TEST_VPN_INTERNAL_DNS)
+                .addInternalDnsServer(TEST_VPN_INTERNAL_DNS6)
                 .build();
     }
 
@@ -1736,9 +1807,19 @@
 
     private PlatformVpnSnapshot verifySetupPlatformVpn(IkeSessionConfiguration ikeConfig)
             throws Exception {
+        return verifySetupPlatformVpn(ikeConfig, true);
+    }
+
+    private PlatformVpnSnapshot verifySetupPlatformVpn(
+            IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6) throws Exception {
+        if (!mtuSupportsIpv6) {
+            doReturn(IPV6_MIN_MTU - 1).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(),
+                    anyBoolean());
+        }
+
         doReturn(mMockNetworkAgent).when(mTestDeps)
                 .newNetworkAgent(
-                        any(), any(), anyString(), any(), any(), any(), any(), any());
+                        any(), any(), anyString(), any(), any(), any(), any(), any(), any());
 
         final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
         when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
@@ -1768,30 +1849,51 @@
                 ArgumentCaptor.forClass(NetworkAgentConfig.class);
         verify(mTestDeps).newNetworkAgent(
                 any(), any(), anyString(), ncCaptor.capture(), lpCaptor.capture(),
-                any(), nacCaptor.capture(), any());
+                any(), nacCaptor.capture(), any(), any());
 
         // Check LinkProperties
         final LinkProperties lp = lpCaptor.getValue();
-        final List<RouteInfo> expectedRoutes = Arrays.asList(
-                new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/,
-                        TEST_IFACE_NAME, RouteInfo.RTN_UNICAST),
-                new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
-                        TEST_IFACE_NAME, RTN_UNREACHABLE));
-        assertEquals(expectedRoutes, lp.getRoutes());
-
-        // Check internal addresses
+        final List<RouteInfo> expectedRoutes =
+                new ArrayList<>(
+                        Arrays.asList(
+                                new RouteInfo(
+                                        new IpPrefix(Inet4Address.ANY, 0),
+                                        null /* gateway */,
+                                        TEST_IFACE_NAME,
+                                        RouteInfo.RTN_UNICAST)));
         final List<LinkAddress> expectedAddresses =
-                Arrays.asList(new LinkAddress(TEST_VPN_INTERNAL_IP, IP4_PREFIX_LEN));
-        assertEquals(expectedAddresses, lp.getLinkAddresses());
+                new ArrayList<>(
+                        Arrays.asList(new LinkAddress(TEST_VPN_INTERNAL_IP, IP4_PREFIX_LEN)));
+        final List<InetAddress> expectedDns = new ArrayList<>(Arrays.asList(TEST_VPN_INTERNAL_DNS));
 
-        // Check internal DNS
-        assertEquals(Arrays.asList(TEST_VPN_INTERNAL_DNS), lp.getDnsServers());
+        if (mtuSupportsIpv6) {
+            expectedRoutes.add(
+                    new RouteInfo(
+                            new IpPrefix(Inet6Address.ANY, 0),
+                            null /* gateway */,
+                            TEST_IFACE_NAME,
+                            RouteInfo.RTN_UNICAST));
+            expectedAddresses.add(new LinkAddress(TEST_VPN_INTERNAL_IP6, IP6_PREFIX_LEN));
+            expectedDns.add(TEST_VPN_INTERNAL_DNS6);
+        } else {
+            expectedRoutes.add(
+                    new RouteInfo(
+                            new IpPrefix(Inet6Address.ANY, 0),
+                            null /* gateway */,
+                            TEST_IFACE_NAME,
+                            RTN_UNREACHABLE));
+        }
+
+        assertEquals(expectedRoutes, lp.getRoutes());
+        assertEquals(expectedAddresses, lp.getLinkAddresses());
+        assertEquals(expectedDns, lp.getDnsServers());
 
         // Check NetworkCapabilities
         assertEquals(Arrays.asList(TEST_NETWORK), ncCaptor.getValue().getUnderlyingNetworks());
 
         // Check if allowBypass is set or not.
         assertTrue(nacCaptor.getValue().isBypassableVpn());
+        assertTrue(((VpnTransportInfo) ncCaptor.getValue().getTransportInfo()).getBypassable());
 
         return new PlatformVpnSnapshot(vpn, nwCb, ikeCb, childCb);
     }
@@ -1804,10 +1906,23 @@
     }
 
     @Test
+    public void testStartPlatformVpn_mtuDoesNotSupportIpv6() throws Exception {
+        final PlatformVpnSnapshot vpnSnapShot =
+                verifySetupPlatformVpn(
+                        createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+                        false /* mtuSupportsIpv6 */);
+        vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+    }
+
+    @Test
     public void testStartPlatformVpnMobility_mobikeEnabled() throws Exception {
         final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
                 createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
 
+        // Set new MTU on a different network
+        final int newMtu = IPV6_MIN_MTU + 1;
+        doReturn(newMtu).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+
         // Mock network loss and verify a cleanup task is scheduled
         vpnSnapShot.nwCb.onLost(TEST_NETWORK);
         verify(mExecutor).schedule(any(Runnable.class), anyLong(), any());
@@ -1836,6 +1951,61 @@
                 vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
         verify(mMockNetworkAgent)
                 .doSetUnderlyingNetworks(Collections.singletonList(TEST_NETWORK_2));
+        verify(mMockNetworkAgent).doSendLinkProperties(argThat(lp -> lp.getMtu() == newMtu));
+        verify(mMockNetworkAgent, never()).unregister();
+
+        vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
+    }
+
+    @Test
+    public void testStartPlatformVpnMobility_mobikeEnabledMtuDoesNotSupportIpv6() throws Exception {
+        final PlatformVpnSnapshot vpnSnapShot =
+                verifySetupPlatformVpn(
+                        createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+        // Set MTU below 1280
+        final int newMtu = IPV6_MIN_MTU - 1;
+        doReturn(newMtu).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(), anyBoolean());
+
+        // Mock new network available & MOBIKE procedures
+        vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
+        vpnSnapShot.ikeCb.onIkeSessionConnectionInfoChanged(createIkeConnectInfo_2());
+        vpnSnapShot.childCb.onIpSecTransformsMigrated(
+                createIpSecTransform(), createIpSecTransform());
+
+        // Verify removal of IPv6 addresses and routes triggers a network agent restart
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        verify(mTestDeps, times(2))
+                .newNetworkAgent(any(), any(), anyString(), any(), lpCaptor.capture(), any(), any(),
+                        any(), any());
+        verify(mMockNetworkAgent).unregister();
+        // mMockNetworkAgent is an old NetworkAgent, so it won't update LinkProperties after
+        // unregistering.
+        verify(mMockNetworkAgent, never()).doSendLinkProperties(any());
+
+        final LinkProperties lp = lpCaptor.getValue();
+
+        for (LinkAddress addr : lp.getLinkAddresses()) {
+            if (addr.isIpv6()) {
+                fail("IPv6 address found on VPN with MTU < IPv6 minimum MTU");
+            }
+        }
+
+        for (InetAddress dnsAddr : lp.getDnsServers()) {
+            if (dnsAddr instanceof Inet6Address) {
+                fail("IPv6 DNS server found on VPN with MTU < IPv6 minimum MTU");
+            }
+        }
+
+        for (RouteInfo routeInfo : lp.getRoutes()) {
+            if (routeInfo.getDestinationLinkAddress().isIpv6()
+                    && !routeInfo.isIPv6UnreachableDefault()) {
+                fail("IPv6 route found on VPN with MTU < IPv6 minimum MTU");
+            }
+        }
+
+        assertEquals(newMtu, lp.getMtu());
 
         vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
     }
@@ -1882,26 +2052,7 @@
         vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
     }
 
-    private void verifyHandlingNetworkLoss() throws Exception {
-        final ArgumentCaptor<LinkProperties> lpCaptor =
-                ArgumentCaptor.forClass(LinkProperties.class);
-        verify(mMockNetworkAgent).doSendLinkProperties(lpCaptor.capture());
-        final LinkProperties lp = lpCaptor.getValue();
-
-        assertNull(lp.getInterfaceName());
-        final List<RouteInfo> expectedRoutes = Arrays.asList(
-                new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /*gateway*/,
-                        null /*iface*/, RTN_UNREACHABLE),
-                new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
-                        null /*iface*/, RTN_UNREACHABLE));
-        assertEquals(expectedRoutes, lp.getRoutes());
-    }
-
-    @Test
-    public void testStartPlatformVpnHandlesNetworkLoss_mobikeEnabled() throws Exception {
-        final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
-                createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
-
+    private void verifyHandlingNetworkLoss(PlatformVpnSnapshot vpnSnapShot) throws Exception {
         // Forget the #sendLinkProperties during first setup.
         reset(mMockNetworkAgent);
 
@@ -1915,21 +2066,142 @@
         verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
         runnableCaptor.getValue().run();
 
-        verifyHandlingNetworkLoss();
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        verify(mMockNetworkAgent).doSendLinkProperties(lpCaptor.capture());
+        final LinkProperties lp = lpCaptor.getValue();
+
+        assertNull(lp.getInterfaceName());
+        final List<RouteInfo> expectedRoutes = Arrays.asList(
+                new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null /* gateway */,
+                        null /* iface */, RTN_UNREACHABLE),
+                new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /* gateway */,
+                        null /* iface */, RTN_UNREACHABLE));
+        assertEquals(expectedRoutes, lp.getRoutes());
+
+        verify(mMockNetworkAgent).unregister();
+    }
+
+    @Test
+    public void testStartPlatformVpnHandlesNetworkLoss_mobikeEnabled() throws Exception {
+        final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+                createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+        verifyHandlingNetworkLoss(vpnSnapShot);
     }
 
     @Test
     public void testStartPlatformVpnHandlesNetworkLoss_mobikeDisabled() throws Exception {
         final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
                 createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+        verifyHandlingNetworkLoss(vpnSnapShot);
+    }
 
-        // Forget the #sendLinkProperties during first setup.
-        reset(mMockNetworkAgent);
+    private ConnectivityDiagnosticsCallback getConnectivityDiagCallback() {
+        final ArgumentCaptor<ConnectivityDiagnosticsCallback> cdcCaptor =
+                ArgumentCaptor.forClass(ConnectivityDiagnosticsCallback.class);
+        verify(mCdm).registerConnectivityDiagnosticsCallback(
+                any(), any(), cdcCaptor.capture());
+        return cdcCaptor.getValue();
+    }
 
-        // Mock network loss
-        vpnSnapShot.nwCb.onLost(TEST_NETWORK);
+    private DataStallReport createDataStallReport() {
+        return new DataStallReport(TEST_NETWORK, 1234 /* reportTimestamp */,
+                1 /* detectionMethod */, new LinkProperties(), new NetworkCapabilities(),
+                new PersistableBundle());
+    }
 
-        verifyHandlingNetworkLoss();
+    private void verifyMobikeTriggered(List<Network> expected) {
+        final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+        verify(mIkeSessionWrapper).setNetwork(networkCaptor.capture());
+        assertEquals(expected, Collections.singletonList(networkCaptor.getValue()));
+    }
+
+    @Test
+    public void testDataStallInIkev2VpnMobikeDisabled() throws Exception {
+        verifySetupPlatformVpn(
+                createIkeConfig(createIkeConnectInfo(), false /* isMobikeEnabled */));
+
+        doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+        final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+                getConnectivityDiagCallback();
+        final DataStallReport report = createDataStallReport();
+        connectivityDiagCallback.onDataStallSuspected(report);
+
+        // Should not trigger MOBIKE if MOBIKE is not enabled
+        verify(mIkeSessionWrapper, never()).setNetwork(any());
+    }
+
+    @Test
+    public void testDataStallInIkev2VpnRecoveredByMobike() throws Exception {
+        final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+                createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+        doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+        final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+                getConnectivityDiagCallback();
+        final DataStallReport report = createDataStallReport();
+        connectivityDiagCallback.onDataStallSuspected(report);
+
+        // Verify MOBIKE is triggered
+        verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+
+        // Expect to skip other data stall event if MOBIKE was started.
+        reset(mIkeSessionWrapper);
+        connectivityDiagCallback.onDataStallSuspected(report);
+        verify(mIkeSessionWrapper, never()).setNetwork(any());
+
+        reset(mIkev2SessionCreator);
+
+        // Send validation status update.
+        // Recovered and get network validated. It should not trigger the ike session reset.
+        ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_VALID);
+        verify(mIkev2SessionCreator, never()).createIkeSession(
+                any(), any(), any(), any(), any(), any());
+
+        // Send invalid result to verify no ike session reset since the data stall suspected
+        // variables(timer counter and boolean) was reset.
+        ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+        final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
+        runnableCaptor.getValue().run();
+        verify(mIkev2SessionCreator, never()).createIkeSession(
+                any(), any(), any(), any(), any(), any());
+    }
+
+    @Test
+    public void testDataStallInIkev2VpnNotRecoveredByMobike() throws Exception {
+        final PlatformVpnSnapshot vpnSnapShot = verifySetupPlatformVpn(
+                createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */));
+
+        final ConnectivityDiagnosticsCallback connectivityDiagCallback =
+                getConnectivityDiagCallback();
+
+        doReturn(TEST_NETWORK).when(mMockNetworkAgent).getNetwork();
+        final DataStallReport report = createDataStallReport();
+        connectivityDiagCallback.onDataStallSuspected(report);
+
+        verifyMobikeTriggered(vpnSnapShot.vpn.mNetworkCapabilities.getUnderlyingNetworks());
+
+        reset(mIkev2SessionCreator);
+
+        // Send validation status update should result in ike session reset.
+        ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+
+        // Verify reset is scheduled and run.
+        final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).schedule(runnableCaptor.capture(), anyLong(), any());
+
+        // Another invalid status reported should not trigger other scheduled recovery.
+        reset(mExecutor);
+        ((Vpn.IkeV2VpnRunner) vpnSnapShot.vpn.mVpnRunner).onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+        verify(mExecutor, never()).schedule(runnableCaptor.capture(), anyLong(), any());
+
+        runnableCaptor.getValue().run();
+        verify(mIkev2SessionCreator).createIkeSession(any(), any(), any(), any(), any(), any());
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
index fdb4d4a..7d800d8 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTests.java
@@ -18,20 +18,25 @@
 
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
 
 import android.util.Log;
 
 import com.android.net.module.util.HexDump;
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.Inet4Address;
@@ -76,14 +81,7 @@
         Inet4Address addr = record.getInet4Address();
         assertEquals("/10.1.2.3", addr.toString());
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
-
-        packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
-        byte[] dataOut = packet.getData();
-
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         assertEquals(dataInText, dataOutText);
@@ -120,14 +118,7 @@
         Inet6Address addr = record.getInet6Address();
         assertEquals("/aabb:ccdd:1122:3344:a0b0:c0d0:1020:3040", addr.toString());
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
-
-        packet = writer.getPacket(MULTICAST_IPV6_ADDRESS);
-        byte[] dataOut = packet.getData();
-
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         assertEquals(dataInText, dataOutText);
@@ -164,14 +155,7 @@
         Inet4Address addr = record.getInet4Address();
         assertEquals("/16.32.48.64", addr.toString());
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
-
-        packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
-        byte[] dataOut = packet.getData();
-
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         final byte[] expectedDataIn =
@@ -212,14 +196,7 @@
         assertFalse(record.hasSubtype());
         assertNull(record.getSubtype());
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
-
-        packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
-        byte[] dataOut = packet.getData();
-
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         assertEquals(dataInText, dataOutText);
@@ -260,20 +237,90 @@
         assertEquals(1, record.getServicePriority());
         assertEquals(255, record.getServiceWeight());
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
-
-        packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
-        byte[] dataOut = packet.getData();
-
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         assertEquals(dataInText, dataOutText);
     }
 
     @Test
+    public void testAnyRecord() throws IOException {
+        final byte[] dataIn = HexDump.hexStringToByteArray(
+                "047465737407616E64726F696403636F6D0000FF0001000000000000");
+        assertNotNull(dataIn);
+        String dataInText = HexDump.dumpHexString(dataIn, 0, dataIn.length);
+
+        // Decode
+        DatagramPacket packet = new DatagramPacket(dataIn, dataIn.length);
+        MdnsPacketReader reader = new MdnsPacketReader(packet);
+
+        String[] name = reader.readLabels();
+        assertNotNull(name);
+        assertEquals(3, name.length);
+        String fqdn = MdnsRecord.labelsToString(name);
+        assertEquals("test.android.com", fqdn);
+
+        int type = reader.readUInt16();
+        assertEquals(MdnsRecord.TYPE_ANY, type);
+
+        MdnsAnyRecord record = new MdnsAnyRecord(name, reader);
+
+        String dataOutText = toHex(record);
+        Log.d(TAG, dataOutText);
+
+        assertEquals(dataInText, dataOutText);
+    }
+
+    @Test
+    public void testNsecRecord() throws IOException {
+        final byte[] dataIn = HexDump.hexStringToByteArray(
+                // record.android.com
+                "067265636F726407616E64726F696403636F6D00"
+                        // Type 0x002f (NSEC), cache flush set on class IN (0x8001)
+                        + "002F8001"
+                        // TTL 0x0000003c (60 secs)
+                        + "0000003C"
+                        // Data length
+                        + "003C"
+                        // nextdomain.android.com
+                        + "0A6E657874646F6D61696E07616E64726F696403636F6D00"
+                        // Type bitmaps: window block 0x00, bitmap length 0x05,
+                        // bits 16 (TXT) and 33 (SRV) set: 0x0000800040
+                        + "00050000800040"
+                        // For 1234, 4*256 + 210 = 1234, so window block 0x04, bitmap length 27/0x1B
+                        // (26*8 + 2 = 210, need 27 bytes to set bit 210),
+                        // bit 2 set on byte 27 (0x20).
+                        + "041B000000000000000000000000000000000000000000000000000020");
+        assertNotNull(dataIn);
+        String dataInText = HexDump.dumpHexString(dataIn, 0, dataIn.length);
+
+        // Decode
+        DatagramPacket packet = new DatagramPacket(dataIn, dataIn.length);
+        MdnsPacketReader reader = new MdnsPacketReader(packet);
+
+        String[] name = reader.readLabels();
+        assertNotNull(name);
+        assertEquals(3, name.length);
+        String fqdn = MdnsRecord.labelsToString(name);
+        assertEquals("record.android.com", fqdn);
+
+        int type = reader.readUInt16();
+        assertEquals(MdnsRecord.TYPE_NSEC, type);
+
+        MdnsNsecRecord record = new MdnsNsecRecord(name, reader);
+        assertTrue(record.getCacheFlush());
+        assertEquals(60_000L, record.getTtl());
+        assertEquals("nextdomain.android.com", MdnsRecord.labelsToString(record.getNextDomain()));
+        assertArrayEquals(new int[] { MdnsRecord.TYPE_TXT,
+                MdnsRecord.TYPE_SRV,
+                // Non-existing record type, > 256
+                1234 }, record.getTypes());
+
+        String dataOutText = toHex(record);
+        assertEquals(dataInText, dataOutText);
+    }
+
+    @Test
     public void testTextRecord() throws IOException {
         final byte[] dataIn = HexDump.hexStringToByteArray(
                 "0474657374000010"
@@ -309,16 +356,72 @@
         assertEquals("b=1234567890", strings.get(1));
         assertEquals("xyz=!@#$", strings.get(2));
 
-        // Encode
-        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
-        record.write(writer, record.getReceiptTime());
+        List<TextEntry> entries = record.getEntries();
+        assertNotNull(entries);
+        assertEquals(3, entries.size());
 
-        packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
-        byte[] dataOut = packet.getData();
+        assertEquals(new TextEntry("a", "hello there"), entries.get(0));
+        assertEquals(new TextEntry("b", "1234567890"), entries.get(1));
+        assertEquals(new TextEntry("xyz", "!@#$"), entries.get(2));
 
-        String dataOutText = HexDump.dumpHexString(dataOut, 0, packet.getLength());
+        String dataOutText = toHex(record);
         Log.d(TAG, dataOutText);
 
         assertEquals(dataInText, dataOutText);
     }
+
+    private static String toHex(MdnsRecord record) throws IOException {
+        MdnsPacketWriter writer = new MdnsPacketWriter(MAX_PACKET_SIZE);
+        record.write(writer, record.getReceiptTime());
+
+        // The address does not matter as only the data is used
+        final DatagramPacket packet = writer.getPacket(MULTICAST_IPV4_ADDRESS);
+        final byte[] dataOut = packet.getData();
+
+        return HexDump.dumpHexString(dataOut, 0, packet.getLength());
+    }
+
+    @Test
+    public void textRecord_recordDoesNotHaveDataOfGivenLength_throwsEOFException()
+            throws Exception {
+        final byte[] dataIn = HexDump.hexStringToByteArray(
+                "0474657374000010"
+                        + "000100001194000D"
+                        + "0D613D68656C6C6F" //The TXT entry starts with length of 13, but only 12
+                        + "2074686572"); // characters are following it.
+        DatagramPacket packet = new DatagramPacket(dataIn, dataIn.length);
+        MdnsPacketReader reader = new MdnsPacketReader(packet);
+        String[] name = reader.readLabels();
+        MdnsRecord.labelsToString(name);
+        reader.readUInt16();
+
+        assertThrows(EOFException.class, () -> new MdnsTextRecord(name, reader));
+    }
+
+    @Test
+    public void textRecord_entriesIncludeNonUtf8Bytes_returnsTheSameUtf8Bytes() throws Exception {
+        final byte[] dataIn = HexDump.hexStringToByteArray(
+                "0474657374000010"
+                        + "0001000011940024"
+                        + "0D613D68656C6C6F"
+                        + "2074686572650C62"
+                        + "3D31323334353637"
+                        + "3839300878797A3D"
+                        + "FFEFDFCF");
+        DatagramPacket packet = new DatagramPacket(dataIn, dataIn.length);
+        MdnsPacketReader reader = new MdnsPacketReader(packet);
+        String[] name = reader.readLabels();
+        MdnsRecord.labelsToString(name);
+        reader.readUInt16();
+
+        MdnsTextRecord record = new MdnsTextRecord(name, reader);
+
+        List<TextEntry> entries = record.getEntries();
+        assertNotNull(entries);
+        assertEquals(3, entries.size());
+        assertEquals(new TextEntry("a", "hello there"), entries.get(0));
+        assertEquals(new TextEntry("b", "1234567890"), entries.get(1));
+        assertEquals(new TextEntry("xyz", HexDump.hexStringToByteArray("FFEFDFCF")),
+                entries.get(2));
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
index ea9156c..02e00c2 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -26,11 +26,14 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
+import android.net.InetAddresses;
+
 import com.android.net.module.util.HexDump;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -106,9 +109,49 @@
             + "63616C0000018001000000780004C0A8010A000001800100000078"
             + "0004C0A8010A00000000000000");
 
-    private static final String DUMMY_CAST_SERVICE_NAME = "_googlecast";
-    private static final String[] DUMMY_CAST_SERVICE_TYPE =
-            new String[] {DUMMY_CAST_SERVICE_NAME, "_tcp", "local"};
+    // Expected to contain two SRV records which point to the same hostname.
+    private static final byte[] matterDuplicateHostname = HexDump.hexStringToByteArray(
+            "00008000000000080000000A095F7365727669636573075F646E732D73"
+            + "64045F756470056C6F63616C00000C000100000078000F075F6D61"
+            + "74746572045F746370C023C00C000C000100000078001A125F4943"
+            + "324639453337374632454139463430045F737562C034C034000C00"
+            + "0100000078002421433246394533373746324541394634302D3030"
+            + "3030303030304534443041334641C034C04F000C00010000007800"
+            + "02C075C00C000C0001000000780002C034C00C000C000100000078"
+            + "0015125F4941413035363731333439334135343144C062C034000C"
+            + "000100000078002421414130353637313334393341353431442D30"
+            + "303030303030304331324446303344C034C0C1000C000100000078"
+            + "0002C0E2C075002100010000007800150000000015A40C33433631"
+            + "3035304338394638C023C07500100001000011940015084352493D"
+            + "35303030074352413D33303003543D31C126001C00010000007800"
+            + "10FE800000000000003E6105FFFE0C89F8C126001C000100000078"
+            + "00102605A601A84657003E6105FFFE0C89F8C12600010001000000"
+            + "780004C0A8018AC0E2002100010000007800080000000015A4C126"
+            + "C0E200100001000011940015084352493D35303030074352413D33"
+            + "303003543D31C126001C0001000000780010FE800000000000003E"
+            + "6105FFFE0C89F8C126001C00010000007800102605A601A8465700"
+            + "3E6105FFFE0C89F8C12600010001000000780004C0A8018A313035"
+            + "304338394638C02300010001000000780004C0A8018AC0A0001000"
+            + "0100001194003A0E56503D36353532312B3332373639084352493D"
+            + "35303030074352413D33303003543D3106443D3236353704434D3D"
+            + "320550483D33360350493D21433246394533373746324541394634"
+            + "302D30303030303030304534443041334641C0F700210001000000"
+            + "7800150000000015A40C334336313035304338394638C023214332"
+            + "46394533373746324541394634302D303030303030303045344430"
+            + "41334641C0F700100001000011940015084352493D353030300743"
+            + "52413D33303003543D310C334336313035304338394638C023001C"
+            + "0001000000780010FE800000000000003E6105FFFE0C89F80C3343"
+            + "36313035304338394638C023001C00010000007800102605A601A8"
+            + "4657003E6105FFFE0C89F80C334336313035304338394638C02300"
+            + "010001000000780004C0A8018A0000000000000000000000000000"
+            + "000000");
+
+    private static final String CAST_SERVICE_NAME = "_googlecast";
+    private static final String[] CAST_SERVICE_TYPE =
+            new String[] {CAST_SERVICE_NAME, "_tcp", "local"};
+    private static final String MATTER_SERVICE_NAME = "_matter";
+    private static final String[] MATTER_SERVICE_TYPE =
+            new String[] {MATTER_SERVICE_NAME, "_tcp", "local"};
 
     private final List<MdnsResponse> responses = new LinkedList<>();
 
@@ -116,13 +159,13 @@
 
     @Before
     public void setUp() {
-        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, DUMMY_CAST_SERVICE_TYPE);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
         assertNotNull(data);
         DatagramPacket packet = new DatagramPacket(data, data.length);
         packet.setSocketAddress(
                 new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
         responses.clear();
-        int errorCode = decoder.decode(packet, responses);
+        int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
         assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
         assertEquals(1, responses.size());
     }
@@ -135,7 +178,7 @@
         packet.setSocketAddress(
                 new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
         responses.clear();
-        int errorCode = decoder.decode(packet, responses);
+        int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
         assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
         assertEquals(2, responses.size());
     }
@@ -153,7 +196,7 @@
 
         MdnsServiceRecord serviceRecord = response.getServiceRecord();
         String serviceName = serviceRecord.getServiceName();
-        assertEquals(DUMMY_CAST_SERVICE_NAME, serviceName);
+        assertEquals(CAST_SERVICE_NAME, serviceName);
 
         String serviceInstanceName = serviceRecord.getServiceInstanceName();
         assertEquals("Johnny's Chromecast", serviceInstanceName);
@@ -187,14 +230,14 @@
 
     @Test
     public void testDecodeIPv6AnswerPacket() throws IOException {
-        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, DUMMY_CAST_SERVICE_TYPE);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
         assertNotNull(data6);
         DatagramPacket packet = new DatagramPacket(data6, data6.length);
         packet.setSocketAddress(
                 new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
 
         responses.clear();
-        int errorCode = decoder.decode(packet, responses);
+        int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
         assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
 
         MdnsResponse response = responses.get(0);
@@ -234,4 +277,77 @@
         response.setTextRecord(null);
         assertFalse(response.isComplete());
     }
+
+    @Test
+    public void decode_withInterfaceIndex_populatesInterfaceIndex() {
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
+        assertNotNull(data6);
+        DatagramPacket packet = new DatagramPacket(data6, data6.length);
+        packet.setSocketAddress(
+                new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
+
+        responses.clear();
+        int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 10);
+        assertEquals(errorCode, MdnsResponseDecoder.SUCCESS);
+        assertEquals(responses.size(), 1);
+        assertEquals(responses.get(0).getInterfaceIndex(), 10);
+    }
+
+    @Test
+    public void decode_singleHostname_multipleSrvRecords_flagEnabled_multipleCompleteResponses() {
+        //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(true);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE);
+        assertNotNull(matterDuplicateHostname);
+
+        DatagramPacket packet =
+                new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length);
+
+        packet.setSocketAddress(
+                new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
+
+        responses.clear();
+        int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
+        assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
+
+        // This should emit two records:
+        assertEquals(2, responses.size());
+
+        MdnsResponse response1 = responses.get(0);
+        MdnsResponse response2 = responses.get(0);
+
+        // Both of which are complete:
+        assertTrue(response1.isComplete());
+        assertTrue(response2.isComplete());
+
+        // And should both have the same IPv6 address:
+        assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"),
+                response1.getInet6AddressRecord().getInet6Address());
+        assertEquals(InetAddresses.parseNumericAddress("2605:a601:a846:5700:3e61:5ff:fe0c:89f8"),
+                response2.getInet6AddressRecord().getInet6Address());
+    }
+
+    @Test
+    @Ignore("MdnsConfigs is not configurable currently.")
+    public void decode_singleHostname_multipleSrvRecords_flagDisabled_singleCompleteResponse() {
+        //MdnsScannerConfigsFlagsImpl.allowMultipleSrvRecordsPerHost.override(false);
+        MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, MATTER_SERVICE_TYPE);
+        assertNotNull(matterDuplicateHostname);
+
+        DatagramPacket packet =
+                new DatagramPacket(matterDuplicateHostname, matterDuplicateHostname.length);
+
+        packet.setSocketAddress(
+                new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
+
+        responses.clear();
+        int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
+        assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
+
+        // This should emit only two records:
+        assertEquals(2, responses.size());
+
+        // But only the first is complete:
+        assertTrue(responses.get(0).isComplete());
+        assertFalse(responses.get(1).isComplete());
+    }
 }
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
index ae16f2b..771e42c 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
@@ -222,6 +222,19 @@
     }
 
     @Test
+    public void getInterfaceIndex_returnsDefaultValue() {
+        MdnsResponse response = new MdnsResponse(/* now= */ 0);
+        assertEquals(response.getInterfaceIndex(), -1);
+    }
+
+    @Test
+    public void getInterfaceIndex_afterSet_returnsValue() {
+        MdnsResponse response = new MdnsResponse(/* now= */ 0);
+        response.setInterfaceIndex(5);
+        assertEquals(response.getInterfaceIndex(), 5);
+    }
+
+    @Test
     public void mergeRecordsFrom_indicates_change_on_ipv4_address() throws IOException {
         MdnsResponse response = makeMdnsResponse(
                 0,
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceInfoTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceInfoTest.java
new file mode 100644
index 0000000..ebdb73f
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceInfoTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 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.server.connectivity.mdns;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Map;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+public class MdnsServiceInfoTest {
+    @Test
+    public void constructor_createWithOnlyTextStrings_correctAttributes() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of("vn=Google Inc.", "mn=Google Nest Hub Max"),
+                        /* textEntries= */ null);
+
+        assertTrue(info.getAttributeByKey("vn").equals("Google Inc."));
+        assertTrue(info.getAttributeByKey("mn").equals("Google Nest Hub Max"));
+    }
+
+    @Test
+    public void constructor_createWithOnlyTextEntries_correctAttributes() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        /* textStrings= */ null,
+                        List.of(MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
+                                MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max")));
+
+        assertTrue(info.getAttributeByKey("vn").equals("Google Inc."));
+        assertTrue(info.getAttributeByKey("mn").equals("Google Nest Hub Max"));
+    }
+
+    @Test
+    public void constructor_createWithBothTextStringsAndTextEntries_acceptsOnlyTextEntries() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of("vn=Alphabet Inc.", "mn=Google Nest Hub Max", "id=12345"),
+                        List.of(
+                                MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
+                                MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max")));
+
+        assertEquals(Map.of("vn", "Google Inc.", "mn", "Google Nest Hub Max"),
+                info.getAttributes());
+    }
+
+    @Test
+    public void constructor_createWithDuplicateKeys_acceptsTheFirstOne() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of("vn=Alphabet Inc.", "mn=Google Nest Hub Max", "id=12345"),
+                        List.of(MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
+                                MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max"),
+                                MdnsServiceInfo.TextEntry.fromString("mn=Google WiFi Router")));
+
+        assertEquals(Map.of("vn", "Google Inc.", "mn", "Google Nest Hub Max"),
+                info.getAttributes());
+    }
+
+    @Test
+    public void getInterfaceIndex_constructorWithDefaultValues_returnsMinusOne() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of());
+
+        assertEquals(info.getInterfaceIndex(), -1);
+    }
+
+    @Test
+    public void getInterfaceIndex_constructorWithInterfaceIndex_returnsProvidedIndex() {
+        MdnsServiceInfo info =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of(),
+                        /* textEntries= */ null,
+                        /* interfaceIndex= */ 20);
+
+        assertEquals(info.getInterfaceIndex(), 20);
+    }
+
+    @Test
+    public void parcelable_canBeParceledAndUnparceled() {
+        Parcel parcel = Parcel.obtain();
+        MdnsServiceInfo beforeParcel =
+                new MdnsServiceInfo(
+                        "my-mdns-service",
+                        new String[] {"_googlecast", "_tcp"},
+                        List.of(),
+                        new String[] {"my-host", "local"},
+                        12345,
+                        "192.168.1.1",
+                        "2001::1",
+                        List.of("vn=Alphabet Inc.", "mn=Google Nest Hub Max", "id=12345"),
+                        List.of(
+                                MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
+                                MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max"),
+                                MdnsServiceInfo.TextEntry.fromString("test=")));
+
+        beforeParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        MdnsServiceInfo afterParcel = MdnsServiceInfo.CREATOR.createFromParcel(parcel);
+
+        assertEquals(beforeParcel.getServiceInstanceName(), afterParcel.getServiceInstanceName());
+        assertArrayEquals(beforeParcel.getServiceType(), afterParcel.getServiceType());
+        assertEquals(beforeParcel.getSubtypes(), afterParcel.getSubtypes());
+        assertArrayEquals(beforeParcel.getHostName(), afterParcel.getHostName());
+        assertEquals(beforeParcel.getPort(), afterParcel.getPort());
+        assertEquals(beforeParcel.getIpv4Address(), afterParcel.getIpv4Address());
+        assertEquals(beforeParcel.getIpv6Address(), afterParcel.getIpv6Address());
+        assertEquals(beforeParcel.getAttributes(), afterParcel.getAttributes());
+    }
+
+    @Test
+    public void textEntry_parcelable_canBeParceledAndUnparceled() {
+        Parcel parcel = Parcel.obtain();
+        TextEntry beforeParcel = new TextEntry("AA", new byte[] {(byte) 0xFF, (byte) 0xFC});
+
+        beforeParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TextEntry afterParcel = TextEntry.CREATOR.createFromParcel(parcel);
+
+        assertEquals(beforeParcel, afterParcel);
+    }
+
+    @Test
+    public void textEntry_fromString_keyValueAreExpected() {
+        TextEntry entry = TextEntry.fromString("AA=xxyyzz");
+
+        assertEquals("AA", entry.getKey());
+        assertArrayEquals(new byte[] {'x', 'x', 'y', 'y', 'z', 'z'}, entry.getValue());
+    }
+
+    @Test
+    public void textEntry_fromStringToString_textUnchanged() {
+        TextEntry entry = TextEntry.fromString("AA=xxyyzz");
+
+        assertEquals("AA=xxyyzz", entry.toString());
+    }
+
+    @Test
+    public void textEntry_fromStringWithoutAssignPunc_noValue() {
+        TextEntry entry = TextEntry.fromString("AA");
+
+        assertEquals("AA", entry.getKey());
+        assertNull(entry.getValue());
+    }
+
+    @Test
+    public void textEntry_fromStringAssignPuncAtBeginning_returnsNull() {
+        TextEntry entry = TextEntry.fromString("=AA");
+
+        assertNull(entry);
+    }
+
+    @Test
+    public void textEntry_fromBytes_keyAndValueAreExpected() {
+        TextEntry entry = TextEntry.fromBytes(
+                new byte[] {'A', 'A', '=', 'x', 'x', 'y', 'y', 'z', 'z'});
+
+        assertEquals("AA", entry.getKey());
+        assertArrayEquals(new byte[] {'x', 'x', 'y', 'y', 'z', 'z'}, entry.getValue());
+    }
+
+    @Test
+    public void textEntry_fromBytesToBytes_textUnchanged() {
+        TextEntry entry = TextEntry.fromBytes(
+                new byte[] {'A', 'A', '=', 'x', 'x', 'y', 'y', 'z', 'z'});
+
+        assertArrayEquals(new byte[] {'A', 'A', '=', 'x', 'x', 'y', 'y', 'z', 'z'},
+                entry.toBytes());
+    }
+
+    @Test
+    public void textEntry_fromBytesWithoutAssignPunc_noValue() {
+        TextEntry entry = TextEntry.fromBytes(new byte[] {'A', 'A'});
+
+        assertEquals("AA", entry.getKey());
+        assertNull(entry.getValue());
+    }
+
+    @Test
+    public void textEntry_fromBytesAssignPuncAtBeginning_returnsNull() {
+        TextEntry entry = TextEntry.fromBytes(new byte[] {'=', 'A', 'A'});
+
+        assertNull(entry);
+    }
+
+    @Test
+    public void textEntry_fromNonUtf8Bytes_keyValueAreExpected() {
+        TextEntry entry = TextEntry.fromBytes(
+                new byte[] {'A', 'A', '=', (byte) 0xFF, (byte) 0xFE, (byte) 0xFD});
+
+        assertEquals("AA", entry.getKey());
+        assertArrayEquals(new byte[] {(byte) 0xFF, (byte) 0xFE, (byte) 0xFD}, entry.getValue());
+    }
+
+    @Test
+    public void textEntry_equals() {
+        assertEquals(new TextEntry("AA", "xxyyzz"), new TextEntry("AA", "xxyyzz"));
+        assertEquals(new TextEntry("BB", "xxyyzz"), new TextEntry("BB", "xxyyzz"));
+        assertEquals(new TextEntry("AA", "XXYYZZ"), new TextEntry("AA", "XXYYZZ"));
+    }
+
+    @Test
+    public void textEntry_fromString_valueIsEmpty() {
+        TextEntry entry = TextEntry.fromString("AA=");
+
+        assertEquals("AA", entry.getKey());
+        assertArrayEquals(new byte[] {}, entry.getValue());
+    }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 5843fd0..462685a 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -18,22 +18,30 @@
 
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.annotation.NonNull;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.text.TextUtils;
+
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
 import com.android.server.connectivity.mdns.MdnsServiceTypeClient.QueryTaskConfig;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
@@ -44,6 +52,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -53,8 +62,8 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.SocketAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -67,8 +76,9 @@
 @RunWith(DevSdkIgnoreRunner.class)
 @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
 public class MdnsServiceTypeClientTests {
-
+    private static final int INTERFACE_INDEX = 999;
     private static final String SERVICE_TYPE = "_googlecast._tcp.local";
+    private static final String[] SERVICE_TYPE_LABELS = TextUtils.split(SERVICE_TYPE, "\\.");
 
     @Mock
     private MdnsServiceBrowserListener mockListenerOne;
@@ -373,15 +383,41 @@
         assertNull(currentThreadExecutor.getAndClearLastScheduledRunnable());
     }
 
+    private static void verifyServiceInfo(MdnsServiceInfo serviceInfo, String serviceName,
+            String[] serviceType, String ipv4Address, String ipv6Address, int port,
+            List<String> subTypes, Map<String, String> attributes, int interfaceIndex) {
+        assertEquals(serviceName, serviceInfo.getServiceInstanceName());
+        assertArrayEquals(serviceType, serviceInfo.getServiceType());
+        assertEquals(ipv4Address, serviceInfo.getIpv4Address());
+        assertEquals(ipv6Address, serviceInfo.getIpv6Address());
+        assertEquals(port, serviceInfo.getPort());
+        assertEquals(subTypes, serviceInfo.getSubtypes());
+        for (String key : attributes.keySet()) {
+            assertEquals(attributes.get(key), serviceInfo.getAttributeByKey(key));
+        }
+        assertEquals(interfaceIndex, serviceInfo.getInterfaceIndex());
+    }
+
     @Test
     public void processResponse_incompleteResponse() {
         client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
 
         MdnsResponse response = mock(MdnsResponse.class);
         when(response.getServiceInstanceName()).thenReturn("service-instance-1");
+        doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
         when(response.isComplete()).thenReturn(false);
 
         client.processResponse(response);
+        verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+                "service-instance-1",
+                SERVICE_TYPE_LABELS,
+                null /* ipv4Address */,
+                null /* ipv6Address */,
+                0 /* port */,
+                List.of() /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                INTERFACE_INDEX);
 
         verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
         verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
@@ -398,8 +434,9 @@
                         "service-instance-1",
                         ipV4Address,
                         5353,
-                        Collections.singletonList("ABCDE"),
-                        Collections.emptyMap());
+                        /* subtype= */ "ABCDE",
+                        Collections.emptyMap(),
+                        /* interfaceIndex= */ 20);
         client.processResponse(initialResponse);
 
         // Process a second response with a different port and updated text attributes.
@@ -408,28 +445,43 @@
                         "service-instance-1",
                         ipV4Address,
                         5354,
-                        Collections.singletonList("ABCDE"),
-                        Collections.singletonMap("key", "value"));
+                        /* subtype= */ "ABCDE",
+                        Collections.singletonMap("key", "value"),
+                        /* interfaceIndex= */ 20);
         client.processResponse(secondResponse);
 
+        // Verify onServiceNameDiscovered was called once for the initial response.
+        verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+                "service-instance-1",
+                SERVICE_TYPE_LABELS,
+                ipV4Address /* ipv4Address */,
+                null /* ipv6Address */,
+                5353 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                20 /* interfaceIndex */);
+
         // Verify onServiceFound was called once for the initial response.
         verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
-        MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(0);
+        MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
         assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
         assertEquals(initialServiceInfo.getIpv4Address(), ipV4Address);
         assertEquals(initialServiceInfo.getPort(), 5353);
         assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
         assertNull(initialServiceInfo.getAttributeByKey("key"));
+        assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
 
         // Verify onServiceUpdated was called once for the second response.
         verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
-        MdnsServiceInfo updatedServiceInfo = serviceInfoCaptor.getAllValues().get(1);
+        MdnsServiceInfo updatedServiceInfo = serviceInfoCaptor.getAllValues().get(2);
         assertEquals(updatedServiceInfo.getServiceInstanceName(), "service-instance-1");
         assertEquals(updatedServiceInfo.getIpv4Address(), ipV4Address);
         assertEquals(updatedServiceInfo.getPort(), 5354);
         assertTrue(updatedServiceInfo.hasSubtypes());
         assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
         assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
+        assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
     }
 
     @Test
@@ -443,8 +495,9 @@
                         "service-instance-1",
                         ipV6Address,
                         5353,
-                        Collections.singletonList("ABCDE"),
-                        Collections.emptyMap());
+                        /* subtype= */ "ABCDE",
+                        Collections.emptyMap(),
+                        /* interfaceIndex= */ 20);
         client.processResponse(initialResponse);
 
         // Process a second response with a different port and updated text attributes.
@@ -453,64 +506,130 @@
                         "service-instance-1",
                         ipV6Address,
                         5354,
-                        Collections.singletonList("ABCDE"),
-                        Collections.singletonMap("key", "value"));
+                        /* subtype= */ "ABCDE",
+                        Collections.singletonMap("key", "value"),
+                        /* interfaceIndex= */ 20);
         client.processResponse(secondResponse);
 
         System.out.println("secondResponses ip"
                 + secondResponse.getInet6AddressRecord().getInet6Address().getHostAddress());
 
+        // Verify onServiceNameDiscovered was called once for the initial response.
+        verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+                "service-instance-1",
+                SERVICE_TYPE_LABELS,
+                null /* ipv4Address */,
+                ipV6Address /* ipv6Address */,
+                5353 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                20 /* interfaceIndex */);
+
         // Verify onServiceFound was called once for the initial response.
         verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
-        MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(0);
+        MdnsServiceInfo initialServiceInfo = serviceInfoCaptor.getAllValues().get(1);
         assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
         assertEquals(initialServiceInfo.getIpv6Address(), ipV6Address);
         assertEquals(initialServiceInfo.getPort(), 5353);
         assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
         assertNull(initialServiceInfo.getAttributeByKey("key"));
+        assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
 
         // Verify onServiceUpdated was called once for the second response.
         verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
-        MdnsServiceInfo updatedServiceInfo = serviceInfoCaptor.getAllValues().get(1);
+        MdnsServiceInfo updatedServiceInfo = serviceInfoCaptor.getAllValues().get(2);
         assertEquals(updatedServiceInfo.getServiceInstanceName(), "service-instance-1");
         assertEquals(updatedServiceInfo.getIpv6Address(), ipV6Address);
         assertEquals(updatedServiceInfo.getPort(), 5354);
         assertTrue(updatedServiceInfo.hasSubtypes());
         assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
         assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
+        assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
+    }
+
+    private void verifyServiceRemovedNoCallback(MdnsServiceBrowserListener listener) {
+        verify(listener, never()).onServiceRemoved(any());
+        verify(listener, never()).onServiceNameRemoved(any());
+    }
+
+    private void verifyServiceRemovedCallback(MdnsServiceBrowserListener listener,
+            String serviceName, String[] serviceType, int interfaceIndex) {
+        verify(listener).onServiceRemoved(argThat(
+                info -> serviceName.equals(info.getServiceInstanceName())
+                        && Arrays.equals(serviceType, info.getServiceType())
+                        && info.getInterfaceIndex() == interfaceIndex));
+        verify(listener).onServiceNameRemoved(argThat(
+                info -> serviceName.equals(info.getServiceInstanceName())
+                        && Arrays.equals(serviceType, info.getServiceType())
+                        && info.getInterfaceIndex() == interfaceIndex));
     }
 
     @Test
-    public void processResponse_goodBye() {
+    public void processResponse_goodBye() throws Exception {
         client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
         client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
 
+        final String serviceName = "service-instance-1";
+        final String ipV6Address = "2000:3333::da6c:63ff:fe7c:7483";
+        // Process the initial response.
+        final MdnsResponse initialResponse =
+                createResponse(
+                        serviceName,
+                        ipV6Address,
+                        5353 /* port */,
+                        /* subtype= */ "ABCDE",
+                        Collections.emptyMap(),
+                        INTERFACE_INDEX);
+        client.processResponse(initialResponse);
         MdnsResponse response = mock(MdnsResponse.class);
-        when(response.getServiceInstanceName()).thenReturn("goodbye-service-instance-name");
-        when(response.isGoodbye()).thenReturn(true);
+        doReturn("goodbye-service").when(response).getServiceInstanceName();
+        doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
+        doReturn(true).when(response).isGoodbye();
         client.processResponse(response);
+        // Verify removed callback won't be called if the service is not existed.
+        verifyServiceRemovedNoCallback(mockListenerOne);
+        verifyServiceRemovedNoCallback(mockListenerTwo);
 
-        verify(mockListenerOne).onServiceRemoved("goodbye-service-instance-name");
-        verify(mockListenerTwo).onServiceRemoved("goodbye-service-instance-name");
+        // Verify removed callback would be called.
+        doReturn(serviceName).when(response).getServiceInstanceName();
+        client.processResponse(response);
+        verifyServiceRemovedCallback(
+                mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
+        verifyServiceRemovedCallback(
+                mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
     }
 
     @Test
-    public void reportExistingServiceToNewlyRegisteredListeners() throws UnknownHostException {
+    public void reportExistingServiceToNewlyRegisteredListeners() throws Exception {
         // Process the initial response.
         MdnsResponse initialResponse =
                 createResponse(
                         "service-instance-1",
                         "192.168.1.1",
                         5353,
-                        Collections.singletonList("ABCDE"),
-                        Collections.emptyMap());
+                        /* subtype= */ "ABCDE",
+                        Collections.emptyMap(),
+                        INTERFACE_INDEX);
         client.processResponse(initialResponse);
 
         client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
 
+        // Verify onServiceNameDiscovered was called once for the existing response.
+        verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+                "service-instance-1",
+                SERVICE_TYPE_LABELS,
+                "192.168.1.1" /* ipv4Address */,
+                null /* ipv6Address */,
+                5353 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                INTERFACE_INDEX);
+
         // Verify onServiceFound was called once for the existing response.
         verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
-        MdnsServiceInfo existingServiceInfo = serviceInfoCaptor.getAllValues().get(0);
+        MdnsServiceInfo existingServiceInfo = serviceInfoCaptor.getAllValues().get(1);
         assertEquals(existingServiceInfo.getServiceInstanceName(), "service-instance-1");
         assertEquals(existingServiceInfo.getIpv4Address(), "192.168.1.1");
         assertEquals(existingServiceInfo.getPort(), 5353);
@@ -527,6 +646,7 @@
 
         // Verify onServiceFound was not called on the newly registered listener after the existing
         // response is gone.
+        verify(mockListenerTwo, never()).onServiceNameDiscovered(any(MdnsServiceInfo.class));
         verify(mockListenerTwo, never()).onServiceFound(any(MdnsServiceInfo.class));
     }
 
@@ -540,9 +660,9 @@
 
         // Process the initial response.
         MdnsResponse initialResponse =
-                createResponse(
+                createMockResponse(
                         serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
-                        Map.of());
+                        Map.of(), INTERFACE_INDEX);
         client.processResponse(initialResponse);
 
         // Clear the scheduled runnable.
@@ -552,8 +672,8 @@
         when(initialResponse.getServiceRecord().getRemainingTTL(anyLong())).thenReturn((long) 0);
         firstMdnsTask.run();
 
-        // Verify onServiceRemoved was not called.
-        verify(mockListenerOne, never()).onServiceRemoved(serviceInstanceName);
+        // Verify removed callback was not called.
+        verifyServiceRemovedNoCallback(mockListenerOne);
     }
 
     @Test
@@ -574,9 +694,9 @@
 
         // Process the initial response.
         MdnsResponse initialResponse =
-                createResponse(
+                createMockResponse(
                         serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
-                        Map.of());
+                        Map.of(), INTERFACE_INDEX);
         client.processResponse(initialResponse);
 
         // Clear the scheduled runnable.
@@ -586,15 +706,16 @@
         when(initialResponse.getServiceRecord().getRemainingTTL(anyLong())).thenReturn((long) 1000);
         firstMdnsTask.run();
 
-        // Verify onServiceRemoved was not called.
-        verify(mockListenerOne, never()).onServiceRemoved(serviceInstanceName);
+        // Verify removed callback was not called.
+        verifyServiceRemovedNoCallback(mockListenerOne);
 
         // Simulate the case where the response is after TTL.
         when(initialResponse.getServiceRecord().getRemainingTTL(anyLong())).thenReturn((long) 0);
         firstMdnsTask.run();
 
-        // Verify onServiceRemoved was called.
-        verify(mockListenerOne, times(1)).onServiceRemoved(serviceInstanceName);
+        // Verify removed callback was called.
+        verifyServiceRemovedCallback(
+                mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
     }
 
     @Test
@@ -613,9 +734,9 @@
 
         // Process the initial response.
         MdnsResponse initialResponse =
-                createResponse(
+                createMockResponse(
                         serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
-                        Map.of());
+                        Map.of(), INTERFACE_INDEX);
         client.processResponse(initialResponse);
 
         // Clear the scheduled runnable.
@@ -625,8 +746,8 @@
         when(initialResponse.getServiceRecord().getRemainingTTL(anyLong())).thenReturn((long) 0);
         firstMdnsTask.run();
 
-        // Verify onServiceRemoved was not called.
-        verify(mockListenerOne, never()).onServiceRemoved(serviceInstanceName);
+        // Verify removed callback was not called.
+        verifyServiceRemovedNoCallback(mockListenerOne);
     }
 
     @Test
@@ -647,9 +768,9 @@
 
         // Process the initial response.
         MdnsResponse initialResponse =
-                createResponse(
+                createMockResponse(
                         serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
-                        Map.of());
+                        Map.of(), INTERFACE_INDEX);
         client.processResponse(initialResponse);
 
         // Clear the scheduled runnable.
@@ -659,8 +780,117 @@
         when(initialResponse.getServiceRecord().getRemainingTTL(anyLong())).thenReturn((long) 0);
         firstMdnsTask.run();
 
-        // Verify onServiceRemoved was not called.
-        verify(mockListenerOne, times(1)).onServiceRemoved(serviceInstanceName);
+        // Verify removed callback was called.
+        verifyServiceRemovedCallback(
+                mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
+    }
+
+    @Test
+    public void testProcessResponse_InOrder() throws Exception {
+        final String serviceName = "service-instance";
+        final String ipV4Address = "192.0.2.0";
+        final String ipV6Address = "2001:db8::";
+        client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
+        InOrder inOrder = inOrder(mockListenerOne);
+
+        // Process the initial response which is incomplete.
+        final MdnsResponse initialResponse =
+                createResponse(
+                        serviceName,
+                        null,
+                        5353,
+                        "ABCDE" /* subtype */,
+                        Collections.emptyMap(),
+                        INTERFACE_INDEX);
+        client.processResponse(initialResponse);
+
+        // Process a second response which has ip address to make response become complete.
+        final MdnsResponse secondResponse =
+                createResponse(
+                        serviceName,
+                        ipV4Address,
+                        5353,
+                        "ABCDE" /* subtype */,
+                        Collections.emptyMap(),
+                        INTERFACE_INDEX);
+        client.processResponse(secondResponse);
+
+        // Process a third response with a different ip address, port and updated text attributes.
+        final MdnsResponse thirdResponse =
+                createResponse(
+                        serviceName,
+                        ipV6Address,
+                        5354,
+                        "ABCDE" /* subtype */,
+                        Collections.singletonMap("key", "value"),
+                        INTERFACE_INDEX);
+        client.processResponse(thirdResponse);
+
+        // Process the last response which is goodbye message.
+        final MdnsResponse lastResponse = mock(MdnsResponse.class);
+        doReturn(serviceName).when(lastResponse).getServiceInstanceName();
+        doReturn(true).when(lastResponse).isGoodbye();
+        client.processResponse(lastResponse);
+
+        // Verify onServiceNameDiscovered was first called for the initial response.
+        inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+                serviceName,
+                SERVICE_TYPE_LABELS,
+                null /* ipv4Address */,
+                null /* ipv6Address */,
+                5353 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                INTERFACE_INDEX);
+
+        // Verify onServiceFound was second called for the second response.
+        inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
+                serviceName,
+                SERVICE_TYPE_LABELS,
+                ipV4Address /* ipv4Address */,
+                null /* ipv6Address */,
+                5353 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", null) /* attributes */,
+                INTERFACE_INDEX);
+
+        // Verify onServiceUpdated was third called for the third response.
+        inOrder.verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(2),
+                serviceName,
+                SERVICE_TYPE_LABELS,
+                ipV4Address /* ipv4Address */,
+                ipV6Address /* ipv6Address */,
+                5354 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", "value") /* attributes */,
+                INTERFACE_INDEX);
+
+        // Verify onServiceRemoved was called for the last response.
+        inOrder.verify(mockListenerOne).onServiceRemoved(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(3),
+                serviceName,
+                SERVICE_TYPE_LABELS,
+                ipV4Address /* ipv4Address */,
+                ipV6Address /* ipv6Address */,
+                5354 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", "value") /* attributes */,
+                INTERFACE_INDEX);
+
+        // Verify onServiceNameRemoved was called for the last response.
+        inOrder.verify(mockListenerOne).onServiceNameRemoved(serviceInfoCaptor.capture());
+        verifyServiceInfo(serviceInfoCaptor.getAllValues().get(4),
+                serviceName,
+                SERVICE_TYPE_LABELS,
+                ipV4Address /* ipv4Address */,
+                ipV6Address /* ipv6Address */,
+                5354 /* port */,
+                Collections.singletonList("ABCDE") /* subTypes */,
+                Collections.singletonMap("key", "value") /* attributes */,
+                INTERFACE_INDEX);
     }
 
     // verifies that the right query was enqueued with the right delay, and send query by executing
@@ -725,14 +955,15 @@
         }
     }
 
-    // Creates a complete mDNS response.
-    private MdnsResponse createResponse(
+    // Creates a mock mDNS response.
+    private MdnsResponse createMockResponse(
             @NonNull String serviceInstanceName,
             @NonNull String host,
             int port,
             @NonNull List<String> subtypes,
-            @NonNull Map<String, String> textAttributes)
-            throws UnknownHostException {
+            @NonNull Map<String, String> textAttributes,
+            int interfaceIndex)
+            throws Exception {
         String[] hostName = new String[]{"hostname"};
         MdnsServiceRecord serviceRecord = mock(MdnsServiceRecord.class);
         when(serviceRecord.getServiceHost()).thenReturn(hostName);
@@ -745,18 +976,23 @@
             when(inetAddressRecord.getInet6Address())
                     .thenReturn((Inet6Address) Inet6Address.getByName(host));
             response.setInet6AddressRecord(inetAddressRecord);
+            response.setInterfaceIndex(interfaceIndex);
         } else {
             when(inetAddressRecord.getInet4Address())
                     .thenReturn((Inet4Address) Inet4Address.getByName(host));
             response.setInet4AddressRecord(inetAddressRecord);
+            response.setInterfaceIndex(interfaceIndex);
         }
 
         MdnsTextRecord textRecord = mock(MdnsTextRecord.class);
         List<String> textStrings = new ArrayList<>();
+        List<TextEntry> textEntries = new ArrayList<>();
         for (Map.Entry<String, String> kv : textAttributes.entrySet()) {
             textStrings.add(kv.getKey() + "=" + kv.getValue());
+            textEntries.add(new TextEntry(kv.getKey(), kv.getValue().getBytes(UTF_8)));
         }
         when(textRecord.getStrings()).thenReturn(textStrings);
+        when(textRecord.getEntries()).thenReturn(textEntries);
 
         response.setServiceRecord(serviceRecord);
         response.setTextRecord(textRecord);
@@ -767,4 +1003,73 @@
         doReturn(new ArrayList<>(subtypes)).when(response).getSubtypes();
         return response;
     }
+
+    // Creates a mDNS response.
+    private MdnsResponse createResponse(
+            @NonNull String serviceInstanceName,
+            @Nullable String host,
+            int port,
+            @NonNull String subtype,
+            @NonNull Map<String, String> textAttributes,
+            int interfaceIndex)
+            throws Exception {
+        MdnsResponse response = new MdnsResponse(0);
+        response.setInterfaceIndex(interfaceIndex);
+
+        // Set PTR record
+        final MdnsPointerRecord pointerRecord = new MdnsPointerRecord(
+                new String[]{subtype, MdnsConstants.SUBTYPE_LABEL, "test"} /* name */,
+                0L /* receiptTimeMillis */,
+                false /* cacheFlush */,
+                120000L /* ttlMillis */,
+                new String[]{serviceInstanceName});
+        response.addPointerRecord(pointerRecord);
+
+        // Set SRV record.
+        final MdnsServiceRecord serviceRecord = new MdnsServiceRecord(
+                new String[] {"service"} /* name */,
+                0L /* receiptTimeMillis */,
+                false /* cacheFlush */,
+                120000L /* ttlMillis */,
+                0 /* servicePriority */,
+                0 /* serviceWeight */,
+                port,
+                new String[]{"hostname"});
+        response.setServiceRecord(serviceRecord);
+
+        // Set A/AAAA record.
+        if (host != null) {
+            if (InetAddresses.parseNumericAddress(host) instanceof Inet6Address) {
+                final MdnsInetAddressRecord inetAddressRecord = new MdnsInetAddressRecord(
+                        new String[] {"address"} /* name */,
+                        0L /* receiptTimeMillis */,
+                        false /* cacheFlush */,
+                        120000L /* ttlMillis */,
+                        Inet6Address.getByName(host));
+                response.setInet6AddressRecord(inetAddressRecord);
+            } else {
+                final MdnsInetAddressRecord inetAddressRecord = new MdnsInetAddressRecord(
+                        new String[] {"address"} /* name */,
+                        0L /* receiptTimeMillis */,
+                        false /* cacheFlush */,
+                        120000L /* ttlMillis */,
+                        Inet4Address.getByName(host));
+                response.setInet4AddressRecord(inetAddressRecord);
+            }
+        }
+
+        // Set TXT record.
+        final List<TextEntry> textEntries = new ArrayList<>();
+        for (Map.Entry<String, String> kv : textAttributes.entrySet()) {
+            textEntries.add(new TextEntry(kv.getKey(), kv.getValue().getBytes(UTF_8)));
+        }
+        final MdnsTextRecord textRecord = new MdnsTextRecord(
+                new String[] {"text"} /* name */,
+                0L /* receiptTimeMillis */,
+                false /* cacheFlush */,
+                120000L /* ttlMillis */,
+                textEntries);
+        response.setTextRecord(textRecord);
+        return response;
+    }
 }
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
index 21ed7eb..b4442a5 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java
@@ -18,13 +18,16 @@
 
 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -45,6 +48,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -490,4 +494,55 @@
         assertFalse(mdnsClient.receivedUnicastResponse);
         assertFalse(mdnsClient.cannotReceiveMulticastResponse.get());
     }
+
+    @Test
+    public void startDiscovery_andPropagateInterfaceIndex_includesInterfaceIndex()
+            throws Exception {
+        //MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(true);
+
+        when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
+        mdnsClient =
+                new MdnsSocketClient(mContext, mockMulticastLock) {
+                    @Override
+                    MdnsSocket createMdnsSocket(int port) {
+                        if (port == MdnsConstants.MDNS_PORT) {
+                            return mockMulticastSocket;
+                        }
+                        return mockUnicastSocket;
+                    }
+                };
+        mdnsClient.setCallback(mockCallback);
+        mdnsClient.startDiscovery();
+
+        verify(mockCallback, timeout(TIMEOUT).atLeastOnce())
+                .onResponseReceived(argThat(response -> response.getInterfaceIndex() == 21));
+    }
+
+    @Test
+    @Ignore("MdnsConfigs is not configurable currently.")
+    public void startDiscovery_andDoNotPropagateInterfaceIndex_doesNotIncludeInterfaceIndex()
+            throws Exception {
+        //MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(false);
+
+        when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
+        mdnsClient =
+                new MdnsSocketClient(mContext, mockMulticastLock) {
+                    @Override
+                    MdnsSocket createMdnsSocket(int port) {
+                        if (port == MdnsConstants.MDNS_PORT) {
+                            return mockMulticastSocket;
+                        }
+                        return mockUnicastSocket;
+                    }
+                };
+        mdnsClient.setCallback(mockCallback);
+        mdnsClient.startDiscovery();
+
+        ArgumentCaptor<MdnsResponse> mdnsResponseCaptor =
+                ArgumentCaptor.forClass(MdnsResponse.class);
+        verify(mockMulticastSocket, never()).getInterfaceIndex();
+        verify(mockCallback, timeout(TIMEOUT).atLeast(1))
+                .onResponseReceived(mdnsResponseCaptor.capture());
+        assertEquals(-1, mdnsResponseCaptor.getValue().getInterfaceIndex());
+    }
 }
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
index 9f11a4b..73dbd38 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketTests.java
@@ -53,7 +53,7 @@
     private SocketAddress socketIPv4Address;
     private SocketAddress socketIPv6Address;
 
-    private byte[] data = new byte[25];
+    private final byte[] data = new byte[25];
     private final DatagramPacket datagramPacket = new DatagramPacket(data, data.length);
     private NetworkInterface networkInterface;
 
@@ -74,14 +74,8 @@
     }
 
     @Test
-    public void testMdnsSocket() throws IOException {
-        mdnsSocket =
-                new MdnsSocket(mockMulticastNetworkInterfaceProvider, MdnsConstants.MDNS_PORT) {
-                    @Override
-                    MulticastSocket createMulticastSocket(int port) throws IOException {
-                        return mockMulticastSocket;
-                    }
-                };
+    public void mdnsSocket_basicFunctionality() throws IOException {
+        mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
         mdnsSocket.send(datagramPacket);
         verify(mockMulticastSocket).setNetworkInterface(networkInterface);
         verify(mockMulticastSocket).send(datagramPacket);
@@ -100,20 +94,14 @@
     }
 
     @Test
-    public void testIPv6OnlyNetwork_IPv6Enabled() throws IOException {
+    public void ipv6OnlyNetwork_ipv6Enabled() throws IOException {
         // Have mockMulticastNetworkInterfaceProvider send back an IPv6Only networkInterfaceWrapper
         networkInterface = createEmptyNetworkInterface();
         when(mockNetworkInterfaceWrapper.getNetworkInterface()).thenReturn(networkInterface);
         when(mockMulticastNetworkInterfaceProvider.getMulticastNetworkInterfaces())
                 .thenReturn(Collections.singletonList(mockNetworkInterfaceWrapper));
 
-        mdnsSocket =
-                new MdnsSocket(mockMulticastNetworkInterfaceProvider, MdnsConstants.MDNS_PORT) {
-                    @Override
-                    MulticastSocket createMulticastSocket(int port) throws IOException {
-                        return mockMulticastSocket;
-                    }
-                };
+        mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
 
         when(mockMulticastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(
                 Collections.singletonList(mockNetworkInterfaceWrapper)))
@@ -130,20 +118,14 @@
     }
 
     @Test
-    public void testIPv6OnlyNetwork_IPv6Toggle() throws IOException {
+    public void ipv6OnlyNetwork_ipv6Toggle() throws IOException {
         // Have mockMulticastNetworkInterfaceProvider send back a networkInterfaceWrapper
         networkInterface = createEmptyNetworkInterface();
         when(mockNetworkInterfaceWrapper.getNetworkInterface()).thenReturn(networkInterface);
         when(mockMulticastNetworkInterfaceProvider.getMulticastNetworkInterfaces())
                 .thenReturn(Collections.singletonList(mockNetworkInterfaceWrapper));
 
-        mdnsSocket =
-                new MdnsSocket(mockMulticastNetworkInterfaceProvider, MdnsConstants.MDNS_PORT) {
-                    @Override
-                    MulticastSocket createMulticastSocket(int port) throws IOException {
-                        return mockMulticastSocket;
-                    }
-                };
+        mdnsSocket = new MdnsSocket(mockMulticastNetworkInterfaceProvider, mockMulticastSocket);
 
         when(mockMulticastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(
                 Collections.singletonList(mockNetworkInterfaceWrapper)))
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index aad80d5..949e0c2 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
@@ -306,35 +305,6 @@
     }
 
     @Test
-    public void testUpdateInterfaceLinkStateForProvisionedInterface() throws Exception {
-        initEthernetNetworkFactory();
-        createAndVerifyProvisionedInterface(TEST_IFACE);
-
-        final boolean retDown = mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */);
-
-        assertTrue(retDown);
-        verifyStop();
-
-        final boolean retUp = mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */);
-
-        assertTrue(retUp);
-    }
-
-    @Test
-    public void testUpdateInterfaceLinkStateForUnprovisionedInterface() throws Exception {
-        initEthernetNetworkFactory();
-        createUnprovisionedInterface(TEST_IFACE);
-
-        final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, false /* up */);
-
-        assertTrue(ret);
-        // There should not be an active IPClient or NetworkAgent.
-        verify(mDeps, never()).makeIpClient(any(), any(), any());
-        verify(mDeps, never())
-                .makeEthernetNetworkAgent(any(), any(), any(), any(), any(), any(), any());
-    }
-
-    @Test
     public void testUpdateInterfaceLinkStateForNonExistingInterface() throws Exception {
         initEthernetNetworkFactory();
 
@@ -346,17 +316,6 @@
     }
 
     @Test
-    public void testUpdateInterfaceLinkStateWithNoChanges() throws Exception {
-        initEthernetNetworkFactory();
-        createAndVerifyProvisionedInterface(TEST_IFACE);
-
-        final boolean ret = mNetFactory.updateInterfaceLinkState(TEST_IFACE, true /* up */);
-
-        assertFalse(ret);
-        verifyNoStopOrStart();
-    }
-
-    @Test
     public void testProvisioningLoss() throws Exception {
         initEthernetNetworkFactory();
         when(mDeps.getNetworkInterfaceByName(TEST_IFACE)).thenReturn(mInterfaceParams);
@@ -390,17 +349,6 @@
     }
 
     @Test
-    public void testLinkPropertiesChanged() throws Exception {
-        initEthernetNetworkFactory();
-        createAndVerifyProvisionedInterface(TEST_IFACE);
-
-        LinkProperties lp = new LinkProperties();
-        mIpClientCallbacks.onLinkPropertiesChange(lp);
-        mLooper.dispatchAll();
-        verify(mNetworkAgent).sendLinkPropertiesImpl(same(lp));
-    }
-
-    @Test
     public void testNetworkUnwanted() throws Exception {
         initEthernetNetworkFactory();
         createAndVerifyProvisionedInterface(TEST_IFACE);
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java b/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
index 9bf893a..de0af94 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetServiceImplTest.java
@@ -312,14 +312,15 @@
     @Test
     public void testEnableInterface() {
         mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).enableInterface(eq(TEST_IFACE),
+        verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(true),
                 any(EthernetCallback.class));
     }
 
     @Test
     public void testDisableInterface() {
         mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).disableInterface(eq(TEST_IFACE), any(EthernetCallback.class));
+        verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(false),
+                any(EthernetCallback.class));
     }
 
     @Test
@@ -384,7 +385,7 @@
         denyManageEthPermission();
 
         mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).enableInterface(eq(TEST_IFACE),
+        verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(true),
                 any(EthernetCallback.class));
     }
 
@@ -395,7 +396,7 @@
         denyManageEthPermission();
 
         mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER);
-        verify(mEthernetTracker).disableInterface(eq(TEST_IFACE),
+        verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(false),
                 any(EthernetCallback.class));
     }
 
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
index ea3d392..5e7f0ff 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
@@ -23,24 +23,15 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.net.EthernetManager;
-import android.net.IEthernetServiceListener;
 import android.net.INetd;
 import android.net.InetAddresses;
-import android.net.InterfaceConfigurationParcel;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
@@ -66,7 +57,6 @@
 
 import java.net.InetAddress;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
@@ -353,22 +343,6 @@
     }
 
     @Test
-    public void testEnableInterfaceCorrectlyCallsFactory() {
-        tracker.enableInterface(TEST_IFACE, NULL_CB);
-        waitForIdle();
-
-        verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(true /* up */));
-    }
-
-    @Test
-    public void testDisableInterfaceCorrectlyCallsFactory() {
-        tracker.disableInterface(TEST_IFACE, NULL_CB);
-        waitForIdle();
-
-        verify(mFactory).updateInterfaceLinkState(eq(TEST_IFACE), eq(false /* up */));
-    }
-
-    @Test
     public void testIsValidTestInterfaceIsFalseWhenTestInterfacesAreNotIncluded() {
         final String validIfaceName = TEST_TAP_PREFIX + "123";
         tracker.setIncludeTestInterfaces(false);
@@ -400,74 +374,4 @@
 
         assertTrue(isValidTestInterface);
     }
-
-    public static class EthernetStateListener extends IEthernetServiceListener.Stub {
-        @Override
-        public void onEthernetStateChanged(int state) { }
-
-        @Override
-        public void onInterfaceStateChanged(String iface, int state, int role,
-                IpConfiguration configuration) { }
-    }
-
-    private InterfaceConfigurationParcel createMockedIfaceParcel(final String ifname,
-            final String hwAddr) {
-        final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel();
-        ifaceParcel.ifName = ifname;
-        ifaceParcel.hwAddr = hwAddr;
-        ifaceParcel.flags = new String[] {INetd.IF_STATE_UP};
-        return ifaceParcel;
-    }
-
-    @Test
-    public void testListenEthernetStateChange() throws Exception {
-        tracker.setIncludeTestInterfaces(true);
-        waitForIdle();
-
-        final String testIface = "testtap123";
-        final String testHwAddr = "11:22:33:44:55:66";
-        final InterfaceConfigurationParcel ifaceParcel = createMockedIfaceParcel(testIface,
-                testHwAddr);
-        when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface});
-        when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel);
-        doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean());
-
-        final AtomicBoolean ifaceUp = new AtomicBoolean(true);
-        doAnswer(inv -> ifaceUp.get()).when(mFactory).hasInterface(testIface);
-        doAnswer(inv ->
-                ifaceUp.get() ? EthernetManager.STATE_LINK_UP : EthernetManager.STATE_ABSENT)
-                .when(mFactory).getInterfaceState(testIface);
-        doAnswer(inv -> {
-            ifaceUp.set(true);
-            return null;
-        }).when(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any());
-        doAnswer(inv -> {
-            ifaceUp.set(false);
-            return null;
-        }).when(mFactory).removeInterface(testIface);
-
-        final EthernetStateListener listener = spy(new EthernetStateListener());
-        tracker.addListener(listener, true /* canUseRestrictedNetworks */);
-        // Check default state.
-        waitForIdle();
-        verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP),
-                anyInt(), any());
-        verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED));
-        reset(listener);
-
-        tracker.setEthernetEnabled(false);
-        waitForIdle();
-        verify(mFactory).removeInterface(eq(testIface));
-        verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED));
-        verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT),
-                anyInt(), any());
-        reset(listener);
-
-        tracker.setEthernetEnabled(true);
-        waitForIdle();
-        verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any());
-        verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED));
-        verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP),
-                anyInt(), any());
-    }
 }
diff --git a/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
index c6852d1..c730856 100644
--- a/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
+++ b/tests/unit/java/com/android/server/net/BpfInterfaceMapUpdaterTest.java
@@ -16,7 +16,14 @@
 
 package com.android.server.net;
 
+import static android.system.OsConstants.EPERM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -27,15 +34,18 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.test.TestLooper;
+import android.system.ErrnoException;
+import android.util.IndentingPrintWriter;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.InterfaceParams;
-import com.android.net.module.util.Struct.U32;
+import com.android.net.module.util.Struct.S32;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.TestBpfMap;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,6 +54,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 @SmallTest
 @RunWith(DevSdkIgnoreRunner.class)
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
@@ -56,13 +69,14 @@
     private final TestLooper mLooper = new TestLooper();
     private BaseNetdUnsolicitedEventListener mListener;
     private BpfInterfaceMapUpdater mUpdater;
-    @Mock private IBpfMap<U32, InterfaceMapValue> mBpfMap;
+    private IBpfMap<S32, InterfaceMapValue> mBpfMap =
+            spy(new TestBpfMap<>(S32.class, InterfaceMapValue.class));
     @Mock private INetd mNetd;
     @Mock private Context mContext;
 
     private class TestDependencies extends BpfInterfaceMapUpdater.Dependencies {
         @Override
-        public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
+        public IBpfMap<S32, InterfaceMapValue> getInterfaceMap() {
             return mBpfMap;
         }
 
@@ -100,7 +114,7 @@
                 ArgumentCaptor.forClass(BaseNetdUnsolicitedEventListener.class);
         verify(mNetd).registerUnsolicitedEventListener(listenerCaptor.capture());
         mListener = listenerCaptor.getValue();
-        verify(mBpfMap).updateEntry(eq(new U32(TEST_INDEX)),
+        verify(mBpfMap).updateEntry(eq(new S32(TEST_INDEX)),
                 eq(new InterfaceMapValue(TEST_INTERFACE_NAME)));
     }
 
@@ -110,7 +124,7 @@
 
         mListener.onInterfaceAdded(TEST_INTERFACE_NAME2);
         mLooper.dispatchAll();
-        verify(mBpfMap).updateEntry(eq(new U32(TEST_INDEX2)),
+        verify(mBpfMap).updateEntry(eq(new S32(TEST_INDEX2)),
                 eq(new InterfaceMapValue(TEST_INTERFACE_NAME2)));
 
         // Check that when onInterfaceRemoved is called, nothing happens.
@@ -118,4 +132,43 @@
         mLooper.dispatchAll();
         verifyNoMoreInteractions(mBpfMap);
     }
+
+    @Test
+    public void testGetIfNameByIndex() throws Exception {
+        mBpfMap.updateEntry(new S32(TEST_INDEX), new InterfaceMapValue(TEST_INTERFACE_NAME));
+        assertEquals(TEST_INTERFACE_NAME, mUpdater.getIfNameByIndex(TEST_INDEX));
+    }
+
+    @Test
+    public void testGetIfNameByIndexNoEntry() {
+        assertNull(mUpdater.getIfNameByIndex(TEST_INDEX));
+    }
+
+    @Test
+    public void testGetIfNameByIndexException() throws Exception {
+        doThrow(new ErrnoException("", EPERM)).when(mBpfMap).getValue(new S32(TEST_INDEX));
+        assertNull(mUpdater.getIfNameByIndex(TEST_INDEX));
+    }
+
+    private void assertDumpContains(final String dump, final String message) {
+        assertTrue(String.format("dump(%s) does not contain '%s'", dump, message),
+                dump.contains(message));
+    }
+
+    private String getDump() {
+        final StringWriter sw = new StringWriter();
+        mUpdater.dump(new IndentingPrintWriter(new PrintWriter(sw), " "));
+        return sw.toString();
+    }
+
+    @Test
+    public void testDump() throws ErrnoException {
+        mBpfMap.updateEntry(new S32(TEST_INDEX), new InterfaceMapValue(TEST_INTERFACE_NAME));
+        mBpfMap.updateEntry(new S32(TEST_INDEX2), new InterfaceMapValue(TEST_INTERFACE_NAME2));
+
+        final String dump = getDump();
+        assertDumpContains(dump, "IfaceIndexNameMap: OK");
+        assertDumpContains(dump, "ifaceIndex=1 ifaceName=test1");
+        assertDumpContains(dump, "ifaceIndex=2 ifaceName=test2");
+    }
 }
diff --git a/tests/unit/java/com/android/server/net/InterfaceMapValueTest.java b/tests/unit/java/com/android/server/net/InterfaceMapValueTest.java
new file mode 100644
index 0000000..ee13d5f
--- /dev/null
+++ b/tests/unit/java/com/android/server/net/InterfaceMapValueTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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.server.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InterfaceMapValueTest {
+    private static final String IF_NAME = "wlan0";
+    private static final byte[] IF_NAME_BYTE = new byte[]{'w', 'l', 'a', 'n', '0'};
+    private static final byte[] IF_NAME_BYTE_WITH_PADDING =
+            new byte[]{'w', 'l', 'a', 'n', '0', 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0}; // IF_NAME_BYTE_WITH_PADDING.length = 16
+    private static final byte[] IF_NAME_BYTE_LONG =
+            new byte[]{'w', 'l', 'a', 'n', '0', 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0}; // IF_NAME_BYTE_LONG.length = 24
+
+    @Test
+    public void testInterfaceMapValueFromString() {
+        final InterfaceMapValue value = new InterfaceMapValue(IF_NAME);
+        assertArrayEquals(IF_NAME_BYTE_WITH_PADDING, value.interfaceName);
+    }
+
+    @Test
+    public void testInterfaceMapValueFromByte() {
+        final InterfaceMapValue value = new InterfaceMapValue(IF_NAME_BYTE_WITH_PADDING);
+        assertArrayEquals(IF_NAME_BYTE_WITH_PADDING, value.interfaceName);
+    }
+
+    @Test
+    public void testInterfaceMapValueFromByteShort() {
+        final InterfaceMapValue value = new InterfaceMapValue(IF_NAME_BYTE);
+        assertArrayEquals(IF_NAME_BYTE_WITH_PADDING, value.interfaceName);
+    }
+
+    @Test
+    public void testInterfaceMapValueFromByteLong() {
+        final InterfaceMapValue value = new InterfaceMapValue(IF_NAME_BYTE_LONG);
+        assertArrayEquals(IF_NAME_BYTE_WITH_PADDING, value.interfaceName);
+    }
+
+    @Test
+    public void testGetInterfaceNameString() {
+        final InterfaceMapValue value = new InterfaceMapValue(IF_NAME_BYTE_WITH_PADDING);
+        assertEquals(IF_NAME, value.getInterfaceNameString());
+    }
+}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 2febd2f..d7c90d8 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -75,12 +75,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -88,7 +90,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.app.AlarmManager;
@@ -140,10 +141,11 @@
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.U32;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.Struct.U8;
 import com.android.net.module.util.bpf.CookieTagMapKey;
 import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.server.BpfNetMaps;
 import com.android.server.net.NetworkStatsService.AlertObserver;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -249,7 +251,11 @@
     private HandlerThread mHandlerThread;
     @Mock
     private LocationPermissionChecker mLocationPermissionChecker;
-    private TestBpfMap<U32, U8> mUidCounterSetMap = spy(new TestBpfMap<>(U32.class, U8.class));
+    private TestBpfMap<S32, U8> mUidCounterSetMap = spy(new TestBpfMap<>(S32.class, U8.class));
+    @Mock
+    private BpfNetMaps mBpfNetMaps;
+    @Mock
+    private SkDestroyListener mSkDestroyListener;
 
     private TestBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap = new TestBpfMap<>(
             CookieTagMapKey.class, CookieTagMapValue.class);
@@ -259,7 +265,8 @@
             StatsMapValue.class);
     private TestBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap = new TestBpfMap<>(
             UidStatsMapKey.class, StatsMapValue.class);
-
+    private TestBpfMap<S32, StatsMapValue> mIfaceStatsMap = new TestBpfMap<>(
+            S32.class, StatsMapValue.class);
     private NetworkStatsService mService;
     private INetworkStatsSession mSession;
     private AlertObserver mAlertObserver;
@@ -344,9 +351,9 @@
 
         final Context context = InstrumentationRegistry.getContext();
         mServiceContext = new MockContext(context);
-        when(mLocationPermissionChecker.checkCallersLocationPermission(
-                any(), any(), anyInt(), anyBoolean(), any())).thenReturn(true);
-        when(sWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
+        doReturn(true).when(mLocationPermissionChecker).checkCallersLocationPermission(
+                any(), any(), anyInt(), anyBoolean(), any());
+        doReturn(TEST_WIFI_NETWORK_KEY).when(sWifiInfo).getNetworkKey();
         mStatsDir = TestIoUtils.createTemporaryDirectory(getClass().getSimpleName());
         mLegacyStatsDir = TestIoUtils.createTemporaryDirectory(
                 getClass().getSimpleName() + "-legacy");
@@ -388,6 +395,10 @@
         verify(mNetd).registerUnsolicitedEventListener(alertObserver.capture());
         mAlertObserver = alertObserver.getValue();
 
+        // Make augmentWithStackedInterfaces returns the interfaces that was passed to it.
+        doAnswer(inv -> ((String[]) inv.getArgument(0)).clone())
+                .when(mStatsFactory).augmentWithStackedInterfaces(any());
+
         // Catch TetheringEventCallback during systemReady().
         ArgumentCaptor<TetheringManager.TetheringEventCallback> tetheringEventCbCaptor =
                 ArgumentCaptor.forClass(TetheringManager.TetheringEventCallback.class);
@@ -474,7 +485,7 @@
             }
 
             @Override
-            public IBpfMap<U32, U8> getUidCounterSetMap() {
+            public IBpfMap<S32, U8> getUidCounterSetMap() {
                 return mUidCounterSetMap;
             }
 
@@ -499,9 +510,25 @@
             }
 
             @Override
+            public IBpfMap<S32, StatsMapValue> getIfaceStatsMap() {
+                return mIfaceStatsMap;
+            }
+
+            @Override
             public boolean isDebuggable() {
                 return mIsDebuggable == Boolean.TRUE;
             }
+
+            @Override
+            public BpfNetMaps makeBpfNetMaps(Context ctx) {
+                return mBpfNetMaps;
+            }
+
+            @Override
+            public SkDestroyListener makeSkDestroyListener(
+                    IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
+                return mSkDestroyListener;
+            }
         };
     }
 
@@ -631,7 +658,7 @@
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.noteUidForeground(UID_RED, true);
         verify(mUidCounterSetMap).updateEntry(
-                eq(new U32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
+                eq(new S32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 6);
 
         forcePollAndWaitForIdle();
@@ -968,8 +995,8 @@
         forcePollAndWaitForIdle();
 
         // Verify service recorded history.
-        assertUidTotal(templateMetered5g, UID_RED, 128L, 2L, 128L, 2L, 0);
-        assertUidTotal(templateNonMetered5g, UID_RED, 256, 3L, 128L, 5L, 0);
+        assertUidTotal(templateMetered5g, UID_RED, 384L, 5L, 256L, 7L, 0);
+        assertUidTotal(templateNonMetered5g, UID_RED, 0L, 0L, 0L, 0L, 0);
     }
 
     @Test
@@ -1079,8 +1106,8 @@
 
     // TODO: support per IMSI state
     private void setMobileRatTypeAndWaitForIdle(int ratType) {
-        when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString()))
-                .thenReturn(ratType);
+        doReturn(ratType).when(mNetworkStatsSubscriptionsMonitor)
+                .getRatTypeForSubscriberId(anyString());
         mService.handleOnCollapsedRatTypeChanged();
         HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
     }
@@ -1218,45 +1245,73 @@
 
     @Test
     public void testUidStatsForTransport() throws Exception {
-        // pretend that network comes online
+        // Setup both wifi and mobile networks, and set mobile network as the default interface.
         mockDefaultSettings();
-        NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
-        mockNetworkStatsSummary(buildEmptyStats());
         mockNetworkStatsUidDetail(buildEmptyStats());
 
-        mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
+        final NetworkStateSnapshot mobileState = buildStateOfTransport(
+                NetworkCapabilities.TRANSPORT_CELLULAR, TYPE_MOBILE,
+                TEST_IFACE2, IMSI_1, null /* wifiNetworkKey */,
+                false /* isTemporarilyNotMetered */, false /* isRoaming */);
+
+        final NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {
+                mobileState, buildWifiState()};
+        mService.notifyNetworkStatus(NETWORKS_MOBILE, states, getActiveIface(states),
                 new UnderlyingNetworkInfo[0]);
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE);
 
-        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+        // Mock traffic on wifi network.
+        final NetworkStats.Entry entry1 = new NetworkStats.Entry(
                 TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 0L);
-        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 1L);
+        final NetworkStats.Entry entry2 = new NetworkStats.Entry(
                 TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 0L);
-        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                DEFAULT_NETWORK_NO, 50L, 5L, 50L, 5L, 1L);
+        final NetworkStats.Entry entry3 = new NetworkStats.Entry(
                 TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 1024L, 8L, 512L, 4L, 0L);
+                DEFAULT_NETWORK_NO, 1024L, 8L, 512L, 4L, 2L);
 
+        final TetherStatsParcel[] emptyTetherStats = {};
+        // The interfaces that expect to be used to query the stats.
+        final String[] wifiIfaces = {TEST_IFACE};
         incrementCurrentTime(HOUR_IN_MILLIS);
         mockDefaultSettings();
-        mockNetworkStatsSummary(buildEmptyStats());
         mockNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
                 .insertEntry(entry1)
                 .insertEntry(entry2)
-                .insertEntry(entry3));
+                .insertEntry(entry3), emptyTetherStats, wifiIfaces);
+
+        // getUidStatsForTransport (through getNetworkStatsUidDetail) adds all operation counts
+        // with active interface, and the interface here is mobile interface, so this test makes
+        // sure these operations are not surfaced in getUidStatsForTransport if the transport
+        // doesn't match them.
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
+        final NetworkStats wifiStats = mService.getUidStatsForTransport(
+                NetworkCapabilities.TRANSPORT_WIFI);
 
-        NetworkStats stats = mService.getUidStatsForTransport(NetworkCapabilities.TRANSPORT_WIFI);
+        assertEquals(3, wifiStats.size());
+        // The iface field of the returned stats should be null because getUidStatsForTransport
+        // clears the interface fields before it returns the result.
+        assertValues(wifiStats, null /* iface */, UID_RED, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, METERED_NO, 50L, 5L, 50L, 5L, 1L);
+        assertValues(wifiStats, null /* iface */, UID_RED, SET_DEFAULT, 0xF00D,
+                METERED_NO, ROAMING_NO, METERED_NO, 50L, 5L, 50L, 5L, 1L);
+        assertValues(wifiStats, null /* iface */, UID_BLUE, SET_DEFAULT, 0xBEEF,
+                METERED_NO, ROAMING_NO, METERED_NO, 1024L, 8L, 512L, 4L, 2L);
 
-        assertEquals(3, stats.size());
-        entry1.operations = 1;
-        entry1.iface = null;
-        assertEquals(entry1, stats.getValues(0, null));
-        entry2.operations = 1;
-        entry2.iface = null;
-        assertEquals(entry2, stats.getValues(1, null));
-        entry3.iface = null;
-        assertEquals(entry3, stats.getValues(2, null));
+        final String[] mobileIfaces = {TEST_IFACE2};
+        mockNetworkStatsUidDetail(buildEmptyStats(), emptyTetherStats, mobileIfaces);
+        final NetworkStats mobileStats = mService.getUidStatsForTransport(
+                NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        assertEquals(2, mobileStats.size());
+        // Verify the operation count stats that caused by incrementOperationCount only appears
+        // on the mobile interface since incrementOperationCount attributes them onto the active
+        // interface.
+        assertValues(mobileStats, null /* iface */, UID_RED, SET_DEFAULT, 0xF00D,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 1);
+        assertValues(mobileStats, null /* iface */, UID_RED, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 1);
     }
 
     @Test
@@ -1296,7 +1351,7 @@
                 .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
         mService.noteUidForeground(UID_RED, true);
         verify(mUidCounterSetMap).updateEntry(
-                eq(new U32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
+                eq(new S32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
 
         forcePollAndWaitForIdle();
@@ -1447,7 +1502,7 @@
                 {buildTetherStatsParcel(TEST_IFACE, 1408L, 10L, 256L, 1L, 0)};
 
         mockNetworkStatsSummary(swIfaceStats);
-        mockNetworkStatsUidDetail(localUidStats, tetherStatsParcels);
+        mockNetworkStatsUidDetail(localUidStats, tetherStatsParcels, INTERFACES_ALL);
         forcePollAndWaitForIdle();
 
         // verify service recorded history
@@ -1524,8 +1579,8 @@
         mUsageCallback.expectOnThresholdReached(request);
 
         // Allow binder to disconnect
-        when(mUsageCallbackBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()))
-                .thenReturn(true);
+        doReturn(true).when(mUsageCallbackBinder)
+                .unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
 
         // Unregister request
         mService.unregisterUsageRequest(request);
@@ -1707,7 +1762,7 @@
     }
 
     private void setCombineSubtypeEnabled(boolean enable) {
-        when(mSettings.getCombineSubtypeEnabled()).thenReturn(enable);
+        doReturn(enable).when(mSettings).getCombineSubtypeEnabled();
         mHandler.post(() -> mContentObserver.onChange(false, Settings.Global
                     .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED)));
         waitForIdle();
@@ -1867,8 +1922,8 @@
      */
     @Test
     public void testEnforceTemplateLocationPermission() throws Exception {
-        when(mLocationPermissionChecker.checkCallersLocationPermission(
-                any(), any(), anyInt(), anyBoolean(), any())).thenReturn(false);
+        doReturn(false).when(mLocationPermissionChecker)
+                .checkCallersLocationPermission(any(), any(), anyInt(), anyBoolean(), any());
         initWifiStats(buildWifiState(true, TEST_IFACE, IMSI_1));
         assertThrows(SecurityException.class, () ->
                 assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0));
@@ -1876,8 +1931,8 @@
         assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
 
-        when(mLocationPermissionChecker.checkCallersLocationPermission(
-                any(), any(), anyInt(), anyBoolean(), any())).thenReturn(true);
+        doReturn(true).when(mLocationPermissionChecker)
+                .checkCallersLocationPermission(any(), any(), anyInt(), anyBoolean(), any());
         assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
@@ -1912,7 +1967,7 @@
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.noteUidForeground(UID_RED, true);
         verify(mUidCounterSetMap).updateEntry(
-                eq(new U32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
+                eq(new S32(UID_RED)), eq(new U8((short) SET_FOREGROUND)));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 6);
 
         forcePollAndWaitForIdle();
@@ -2205,25 +2260,26 @@
     }
 
     private void mockNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
-        when(mStatsFactory.readNetworkStatsSummaryDev()).thenReturn(summary);
+        doReturn(summary).when(mStatsFactory).readNetworkStatsSummaryDev();
     }
 
     private void mockNetworkStatsSummaryXt(NetworkStats summary) throws Exception {
-        when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary);
+        doReturn(summary).when(mStatsFactory).readNetworkStatsSummaryXt();
     }
 
     private void mockNetworkStatsUidDetail(NetworkStats detail) throws Exception {
         final TetherStatsParcel[] tetherStatsParcels = {};
-        mockNetworkStatsUidDetail(detail, tetherStatsParcels);
+        mockNetworkStatsUidDetail(detail, tetherStatsParcels, INTERFACES_ALL);
     }
 
     private void mockNetworkStatsUidDetail(NetworkStats detail,
-            TetherStatsParcel[] tetherStatsParcels) throws Exception {
-        when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL))
-                .thenReturn(detail);
+            TetherStatsParcel[] tetherStatsParcels, String[] ifaces) throws Exception {
+
+        doReturn(detail).when(mStatsFactory)
+                .readNetworkStatsDetail(eq(UID_ALL), aryEq(ifaces), eq(TAG_ALL));
 
         // also include tethering details, since they are folded into UID
-        when(mNetd.tetherGetStats()).thenReturn(tetherStatsParcels);
+        doReturn(tetherStatsParcels).when(mNetd).tetherGetStats();
     }
 
     private void mockDefaultSettings() throws Exception {
@@ -2231,22 +2287,22 @@
     }
 
     private void mockSettings(long bucketDuration, long deleteAge) throws Exception {
-        when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
-        when(mSettings.getPollDelay()).thenReturn(0L);
-        when(mSettings.getSampleEnabled()).thenReturn(true);
-        when(mSettings.getCombineSubtypeEnabled()).thenReturn(false);
+        doReturn(HOUR_IN_MILLIS).when(mSettings).getPollInterval();
+        doReturn(0L).when(mSettings).getPollDelay();
+        doReturn(true).when(mSettings).getSampleEnabled();
+        doReturn(false).when(mSettings).getCombineSubtypeEnabled();
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
-        when(mSettings.getDevConfig()).thenReturn(config);
-        when(mSettings.getXtConfig()).thenReturn(config);
-        when(mSettings.getUidConfig()).thenReturn(config);
-        when(mSettings.getUidTagConfig()).thenReturn(config);
+        doReturn(config).when(mSettings).getDevConfig();
+        doReturn(config).when(mSettings).getXtConfig();
+        doReturn(config).when(mSettings).getUidConfig();
+        doReturn(config).when(mSettings).getUidTagConfig();
 
-        when(mSettings.getGlobalAlertBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getDevPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getXtPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getUidPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
-        when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
+        doReturn(MB_IN_BYTES).when(mSettings).getGlobalAlertBytes(anyLong());
+        doReturn(MB_IN_BYTES).when(mSettings).getDevPersistBytes(anyLong());
+        doReturn(MB_IN_BYTES).when(mSettings).getXtPersistBytes(anyLong());
+        doReturn(MB_IN_BYTES).when(mSettings).getUidPersistBytes(anyLong());
+        doReturn(MB_IN_BYTES).when(mSettings).getUidTagPersistBytes(anyLong());
     }
 
     private void assertStatsFilesExist(boolean exist) {
@@ -2409,13 +2465,13 @@
 
         mAppUidStatsMap.insertEntry(new UidStatsMapKey(uid), new StatsMapValue(10, 10000, 6, 6000));
 
-        mUidCounterSetMap.insertEntry(new U32(uid), new U8((short) 1));
+        mUidCounterSetMap.insertEntry(new S32(uid), new U8((short) 1));
 
         assertTrue(cookieTagMapContainsUid(uid));
         assertTrue(statsMapContainsUid(mStatsMapA, uid));
         assertTrue(statsMapContainsUid(mStatsMapB, uid));
         assertTrue(mAppUidStatsMap.containsKey(new UidStatsMapKey(uid)));
-        assertTrue(mUidCounterSetMap.containsKey(new U32(uid)));
+        assertTrue(mUidCounterSetMap.containsKey(new S32(uid)));
     }
 
     @Test
@@ -2432,14 +2488,14 @@
         assertFalse(statsMapContainsUid(mStatsMapA, UID_BLUE));
         assertFalse(statsMapContainsUid(mStatsMapB, UID_BLUE));
         assertFalse(mAppUidStatsMap.containsKey(new UidStatsMapKey(UID_BLUE)));
-        assertFalse(mUidCounterSetMap.containsKey(new U32(UID_BLUE)));
+        assertFalse(mUidCounterSetMap.containsKey(new S32(UID_BLUE)));
 
         // assert that UID_RED related tag data is still in the maps.
         assertTrue(cookieTagMapContainsUid(UID_RED));
         assertTrue(statsMapContainsUid(mStatsMapA, UID_RED));
         assertTrue(statsMapContainsUid(mStatsMapB, UID_RED));
         assertTrue(mAppUidStatsMap.containsKey(new UidStatsMapKey(UID_RED)));
-        assertTrue(mUidCounterSetMap.containsKey(new U32(UID_RED)));
+        assertTrue(mUidCounterSetMap.containsKey(new S32(UID_RED)));
     }
 
     private void assertDumpContains(final String dump, final String message) {
@@ -2513,4 +2569,49 @@
         assertDumpContains(dump, "uid rxBytes rxPackets txBytes txPackets");
         assertDumpContains(dump, "1002 10000 10 6000 6");
     }
+
+    private void doTestDumpStatsMap(final String expectedIfaceName) throws ErrnoException {
+        initBpfMapsWithTagData(UID_BLUE);
+
+        final String dump = getDump();
+        assertDumpContains(dump, "mStatsMapA: OK");
+        assertDumpContains(dump, "mStatsMapB: OK");
+        assertDumpContains(dump,
+                "ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes rxPackets txBytes txPackets");
+        assertDumpContains(dump, "10 " + expectedIfaceName + " 0x2 1002 0 5000 5 3000 3");
+        assertDumpContains(dump, "10 " + expectedIfaceName + " 0x1 1002 0 5000 5 3000 3");
+    }
+
+    @Test
+    public void testDumpStatsMap() throws ErrnoException {
+        doReturn("wlan0").when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+        doTestDumpStatsMap("wlan0");
+    }
+
+    @Test
+    public void testDumpStatsMapUnknownInterface() throws ErrnoException {
+        doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+        doTestDumpStatsMap("unknown");
+    }
+
+    void doTestDumpIfaceStatsMap(final String expectedIfaceName) throws Exception {
+        mIfaceStatsMap.insertEntry(new S32(10), new StatsMapValue(3, 3000, 3, 3000));
+
+        final String dump = getDump();
+        assertDumpContains(dump, "mIfaceStatsMap: OK");
+        assertDumpContains(dump, "ifaceIndex ifaceName rxBytes rxPackets txBytes txPackets");
+        assertDumpContains(dump, "10 " + expectedIfaceName + " 3000 3 3000 3");
+    }
+
+    @Test
+    public void testDumpIfaceStatsMap() throws Exception {
+        doReturn("wlan0").when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+        doTestDumpIfaceStatsMap("wlan0");
+    }
+
+    @Test
+    public void testDumpIfaceStatsMapUnknownInterface() throws Exception {
+        doReturn(null).when(mBpfInterfaceMapUpdater).getIfNameByIndex(10 /* index */);
+        doTestDumpIfaceStatsMap("unknown");
+    }
 }
diff --git a/tools/Android.bp b/tools/Android.bp
index 1fa93bb..3ce76f6 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -27,14 +27,6 @@
         "gen_jarjar.py",
     ],
     main: "gen_jarjar.py",
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-        },
-    },
     visibility: ["//packages/modules/Connectivity:__subpackages__"],
 }
 
@@ -48,6 +40,7 @@
 java_library {
     name: "jarjar-rules-generator-testjavalib",
     srcs: ["testdata/java/**/*.java"],
+    libs: ["unsupportedappusage"],
     visibility: ["//visibility:private"],
 }
 
@@ -67,6 +60,17 @@
     compile_dex: false,
 }
 
+java_library {
+    name: "framework-connectivity-t.stubs.module_lib-for-test",
+    visibility: ["//visibility:private"],
+    static_libs: [
+        "framework-connectivity-t.stubs.module_lib",
+    ],
+    // Not strictly necessary but specified as this MUST not have generate
+    // a dex jar as that will break the tests.
+    compile_dex: false,
+}
+
 python_test_host {
     name: "jarjar-rules-generator-test",
     srcs: [
@@ -75,17 +79,12 @@
     ],
     data: [
         "testdata/test-jarjar-excludes.txt",
+        // two unsupportedappusage lists with different classes to test using multiple lists
         "testdata/test-unsupportedappusage.txt",
+        "testdata/test-other-unsupportedappusage.txt",
         ":framework-connectivity.stubs.module_lib-for-test",
+        ":framework-connectivity-t.stubs.module_lib-for-test",
         ":jarjar-rules-generator-testjavalib",
     ],
     main: "gen_jarjar_test.py",
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-        },
-    },
 }
diff --git a/tools/gen_jarjar.py b/tools/gen_jarjar.py
index 2ff53fa..eb686ce 100755
--- a/tools/gen_jarjar.py
+++ b/tools/gen_jarjar.py
@@ -28,8 +28,8 @@
 def parse_arguments(argv):
     parser = argparse.ArgumentParser()
     parser.add_argument(
-        '--jars', nargs='+',
-        help='Path to pre-jarjar JAR. Can be followed by multiple space-separated paths.')
+        'jars', nargs='+',
+        help='Path to pre-jarjar JAR. Multiple jars can be specified.')
     parser.add_argument(
         '--prefix', required=True,
         help='Package prefix to use for jarjared classes, '
@@ -37,18 +37,17 @@
     parser.add_argument(
         '--output', required=True, help='Path to output jarjar rules file.')
     parser.add_argument(
-        '--apistubs', nargs='*', default=[],
-        help='Path to API stubs jar. Classes that are API will not be jarjared. Can be followed by '
-             'multiple space-separated paths.')
+        '--apistubs', action='append', default=[],
+        help='Path to API stubs jar. Classes that are API will not be jarjared. Can be repeated to '
+             'specify multiple jars.')
     parser.add_argument(
-        '--unsupportedapi', nargs='*', default=[],
-        help='Path to UnsupportedAppUsage hidden API .txt lists. '
-             'Classes that have UnsupportedAppUsage API will not be jarjared. Can be followed by '
-             'multiple space-separated paths.')
+        '--unsupportedapi',
+        help='Column(:)-separated paths to UnsupportedAppUsage hidden API .txt lists. '
+             'Classes that have UnsupportedAppUsage API will not be jarjared.')
     parser.add_argument(
-        '--excludes', nargs='*', default=[],
-        help='Path to files listing classes that should not be jarjared. Can be followed by '
-             'multiple space-separated paths. '
+        '--excludes', action='append', default=[],
+        help='Path to files listing classes that should not be jarjared. Can be repeated to '
+             'specify multiple files.'
              'Each file should contain one full-match regex per line. Empty lines or lines '
              'starting with "#" are ignored.')
     return parser.parse_args(argv)
@@ -103,8 +102,10 @@
     for apistubs_file in args.apistubs:
         excluded_classes.update(_list_toplevel_jar_classes(apistubs_file))
 
-    for unsupportedapi_file in args.unsupportedapi:
-        excluded_classes.update(_list_hiddenapi_classes(unsupportedapi_file))
+    unsupportedapi_files = (args.unsupportedapi and args.unsupportedapi.split(':')) or []
+    for unsupportedapi_file in unsupportedapi_files:
+        if unsupportedapi_file:
+            excluded_classes.update(_list_hiddenapi_classes(unsupportedapi_file))
 
     exclude_regexes = []
     for exclude_file in args.excludes:
diff --git a/tools/gen_jarjar_test.py b/tools/gen_jarjar_test.py
index 8d8e82b..f5bf499 100644
--- a/tools/gen_jarjar_test.py
+++ b/tools/gen_jarjar_test.py
@@ -31,11 +31,11 @@
 class TestGenJarjar(unittest.TestCase):
     def test_gen_rules(self):
         args = gen_jarjar.parse_arguments([
-            "--jars", "jarjar-rules-generator-testjavalib.jar",
+            "jarjar-rules-generator-testjavalib.jar",
             "--prefix", "jarjar.prefix",
             "--output", "test-output-rules.txt",
             "--apistubs", "framework-connectivity.stubs.module_lib.jar",
-            "--unsupportedapi", "testdata/test-unsupportedappusage.txt",
+            "--unsupportedapi", ":testdata/test-unsupportedappusage.txt",
             "--excludes", "testdata/test-jarjar-excludes.txt",
         ])
         gen_jarjar.make_jarjar_rules(args)
@@ -43,6 +43,39 @@
         with open(args.output) as out:
             lines = out.readlines()
 
+        self.maxDiff = None
+        self.assertListEqual([
+            'rule android.net.IpSecTransform jarjar.prefix.@0\n',
+            'rule android.net.IpSecTransformTest jarjar.prefix.@0\n',
+            'rule android.net.IpSecTransformTest$* jarjar.prefix.@0\n',
+            'rule test.unsupportedappusage.OtherUnsupportedUsageClass jarjar.prefix.@0\n',
+            'rule test.unsupportedappusage.OtherUnsupportedUsageClassTest jarjar.prefix.@0\n',
+            'rule test.unsupportedappusage.OtherUnsupportedUsageClassTest$* jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClass jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClassTest jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClassTest$* jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClass$TestInnerClass jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClass$TestInnerClassTest jarjar.prefix.@0\n',
+            'rule test.utils.TestUtilClass$TestInnerClassTest$* jarjar.prefix.@0\n'
+        ], lines)
+
+    def test_gen_rules_repeated_args(self):
+        args = gen_jarjar.parse_arguments([
+            "jarjar-rules-generator-testjavalib.jar",
+            "--prefix", "jarjar.prefix",
+            "--output", "test-output-rules.txt",
+            "--apistubs", "framework-connectivity.stubs.module_lib.jar",
+            "--apistubs", "framework-connectivity-t.stubs.module_lib.jar",
+            "--unsupportedapi",
+            "testdata/test-unsupportedappusage.txt:testdata/test-other-unsupportedappusage.txt",
+            "--excludes", "testdata/test-jarjar-excludes.txt",
+        ])
+        gen_jarjar.make_jarjar_rules(args)
+
+        with open(args.output) as out:
+            lines = out.readlines()
+
+        self.maxDiff = None
         self.assertListEqual([
             'rule test.utils.TestUtilClass jarjar.prefix.@0\n',
             'rule test.utils.TestUtilClassTest jarjar.prefix.@0\n',
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
new file mode 100644
index 0000000..4d0ed32
--- /dev/null
+++ b/tools/gn2bp/Android.bp.swp
@@ -0,0 +1,25469 @@
+// Copyright (C) 2022 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.
+//
+// This file is automatically generated by gen_android_bp. Do not edit.
+
+// GN: //base/allocator:buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_buildflags__android_arm",
+    cmd: "echo '--flags USE_PARTITION_ALLOC=\"false\" USE_ALLOCATOR_SHIM=\"true\" USE_PARTITION_ALLOC_AS_MALLOC=\"false\" USE_BACKUP_REF_PTR=\"false\" USE_ASAN_BACKUP_REF_PTR=\"false\" USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=\"false\" USE_MTE_CHECKED_PTR=\"false\" FORCE_ENABLE_RAW_PTR_EXCLUSION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator:buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_buildflags__android_arm64",
+    cmd: "echo '--flags USE_PARTITION_ALLOC=\"false\" USE_ALLOCATOR_SHIM=\"true\" USE_PARTITION_ALLOC_AS_MALLOC=\"false\" USE_BACKUP_REF_PTR=\"false\" USE_ASAN_BACKUP_REF_PTR=\"false\" USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=\"false\" USE_MTE_CHECKED_PTR=\"false\" FORCE_ENABLE_RAW_PTR_EXCLUSION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator:buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_buildflags__android_x86",
+    cmd: "echo '--flags USE_PARTITION_ALLOC=\"false\" USE_ALLOCATOR_SHIM=\"true\" USE_PARTITION_ALLOC_AS_MALLOC=\"false\" USE_BACKUP_REF_PTR=\"false\" USE_ASAN_BACKUP_REF_PTR=\"false\" USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=\"false\" USE_MTE_CHECKED_PTR=\"false\" FORCE_ENABLE_RAW_PTR_EXCLUSION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator:buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_buildflags__android_x86_64",
+    cmd: "echo '--flags USE_PARTITION_ALLOC=\"false\" USE_ALLOCATOR_SHIM=\"true\" USE_PARTITION_ALLOC_AS_MALLOC=\"false\" USE_BACKUP_REF_PTR=\"false\" USE_ASAN_BACKUP_REF_PTR=\"false\" USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=\"false\" USE_MTE_CHECKED_PTR=\"false\" FORCE_ENABLE_RAW_PTR_EXCLUSION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator:buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_buildflags__host",
+    cmd: "echo '--flags USE_PARTITION_ALLOC=\"false\" USE_ALLOCATOR_SHIM=\"true\" USE_PARTITION_ALLOC_AS_MALLOC=\"false\" USE_BACKUP_REF_PTR=\"false\" USE_ASAN_BACKUP_REF_PTR=\"false\" USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=\"false\" USE_MTE_CHECKED_PTR=\"false\" FORCE_ENABLE_RAW_PTR_EXCLUSION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromecast_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm",
+    cmd: "echo '--flags PA_IS_CAST_ANDROID=\"false\" PA_IS_CASTOS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromecast_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm64",
+    cmd: "echo '--flags PA_IS_CAST_ANDROID=\"false\" PA_IS_CASTOS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromecast_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86",
+    cmd: "echo '--flags PA_IS_CAST_ANDROID=\"false\" PA_IS_CASTOS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromecast_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86_64",
+    cmd: "echo '--flags PA_IS_CAST_ANDROID=\"false\" PA_IS_CASTOS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromecast_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__host",
+    cmd: "echo '--flags PA_IS_CAST_ANDROID=\"false\" PA_IS_CASTOS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/partition_allocator/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromeos_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm",
+    cmd: "echo '--flags PA_IS_CHROMEOS_ASH=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromeos_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm64",
+    cmd: "echo '--flags PA_IS_CHROMEOS_ASH=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromeos_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86",
+    cmd: "echo '--flags PA_IS_CHROMEOS_ASH=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromeos_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86_64",
+    cmd: "echo '--flags PA_IS_CHROMEOS_ASH=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:chromeos_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__host",
+    cmd: "echo '--flags PA_IS_CHROMEOS_ASH=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/partition_allocator/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:debugging_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm",
+    cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:debugging_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm64",
+    cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:debugging_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86",
+    cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:debugging_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86_64",
+    cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:debugging_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__host",
+    cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:logging_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm",
+    cmd: "echo '--flags PA_ENABLE_LOG_ERROR_NOT_REACHED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:logging_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm64",
+    cmd: "echo '--flags PA_ENABLE_LOG_ERROR_NOT_REACHED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:logging_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86",
+    cmd: "echo '--flags PA_ENABLE_LOG_ERROR_NOT_REACHED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:logging_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86_64",
+    cmd: "echo '--flags PA_ENABLE_LOG_ERROR_NOT_REACHED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:logging_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_logging_buildflags__host",
+    cmd: "echo '--flags PA_ENABLE_LOG_ERROR_NOT_REACHED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/partition_allocator/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc
+cc_library_static {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+    srcs: [
+        "base/allocator/partition_allocator/address_pool_manager.cc",
+        "base/allocator/partition_allocator/address_pool_manager_bitmap.cc",
+        "base/allocator/partition_allocator/address_space_randomization.cc",
+        "base/allocator/partition_allocator/allocation_guard.cc",
+        "base/allocator/partition_allocator/dangling_raw_ptr_checks.cc",
+        "base/allocator/partition_allocator/gwp_asan_support.cc",
+        "base/allocator/partition_allocator/memory_reclaimer.cc",
+        "base/allocator/partition_allocator/oom.cc",
+        "base/allocator/partition_allocator/oom_callback.cc",
+        "base/allocator/partition_allocator/page_allocator.cc",
+        "base/allocator/partition_allocator/page_allocator_internals_posix.cc",
+        "base/allocator/partition_allocator/partition_address_space.cc",
+        "base/allocator/partition_allocator/partition_alloc.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/check.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/cpu.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/debug/alias.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/files/file_util_posix.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/logging.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/pkey.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/rand_util.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/time/time.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/time/time_conversion_posix.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/time/time_now_posix.cc",
+        "base/allocator/partition_allocator/partition_alloc_base/time/time_override.cc",
+        "base/allocator/partition_allocator/partition_alloc_hooks.cc",
+        "base/allocator/partition_allocator/partition_bucket.cc",
+        "base/allocator/partition_allocator/partition_oom.cc",
+        "base/allocator/partition_allocator/partition_page.cc",
+        "base/allocator/partition_allocator/partition_root.cc",
+        "base/allocator/partition_allocator/partition_stats.cc",
+        "base/allocator/partition_allocator/random.cc",
+        "base/allocator/partition_allocator/reservation_offset_table.cc",
+        "base/allocator/partition_allocator/spinning_mutex.cc",
+        "base/allocator/partition_allocator/starscan/metadata_allocator.cc",
+        "base/allocator/partition_allocator/starscan/pcscan.cc",
+        "base/allocator/partition_allocator/starscan/pcscan_internal.cc",
+        "base/allocator/partition_allocator/starscan/pcscan_scheduling.cc",
+        "base/allocator/partition_allocator/starscan/snapshot.cc",
+        "base/allocator/partition_allocator/starscan/stack/stack.cc",
+        "base/allocator/partition_allocator/starscan/stats_collector.cc",
+        "base/allocator/partition_allocator/starscan/write_protector.cc",
+        "base/allocator/partition_allocator/tagging.cc",
+        "base/allocator/partition_allocator/thread_cache.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DIS_PARTITION_ALLOC_IMPL",
+        "-DPA_PCSCAN_STACK_SUPPORTED",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                "base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc",
+                "base/allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                "base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc",
+                "base/allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-march=armv8-a+memtag",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_arm64",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                "base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc",
+                "base/allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                "base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc",
+                "base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc",
+                "base/allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__android_x86_64",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            srcs: [
+                "base/allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc",
+            ],
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__host",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_partition_allocator_chromecast_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_chromeos_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_debugging_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_logging_buildflags__host",
+                "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_PARTITION_ALLOC_AS_MALLOC_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=\"false\" ENABLE_DANGLING_RAW_PTR_CHECKS=\"false\" PUT_REF_COUNT_IN_PREVIOUS_SLOT=\"true\" ENABLE_GWP_ASAN_SUPPORT=\"true\" ENABLE_MTE_CHECKED_PTR_SUPPORT=\"false\" RECORD_ALLOC_INFO=\"false\" USE_FREESLOT_BITMAP=\"false\" GLUE_CORE_POOLS=\"false\" ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=\"false\" STARSCAN=\"true\" PA_USE_BASE_TRACING=\"true\" ENABLE_PKEYS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:partition_alloc_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_PARTITION_ALLOC_AS_MALLOC_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=\"false\" ENABLE_DANGLING_RAW_PTR_CHECKS=\"false\" PUT_REF_COUNT_IN_PREVIOUS_SLOT=\"true\" ENABLE_GWP_ASAN_SUPPORT=\"true\" ENABLE_MTE_CHECKED_PTR_SUPPORT=\"false\" RECORD_ALLOC_INFO=\"false\" USE_FREESLOT_BITMAP=\"false\" GLUE_CORE_POOLS=\"false\" ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=\"false\" STARSCAN=\"true\" PA_USE_BASE_TRACING=\"true\" ENABLE_PKEYS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:partition_alloc_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_PARTITION_ALLOC_AS_MALLOC_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=\"false\" ENABLE_DANGLING_RAW_PTR_CHECKS=\"false\" PUT_REF_COUNT_IN_PREVIOUS_SLOT=\"true\" ENABLE_GWP_ASAN_SUPPORT=\"true\" ENABLE_MTE_CHECKED_PTR_SUPPORT=\"false\" RECORD_ALLOC_INFO=\"false\" USE_FREESLOT_BITMAP=\"false\" GLUE_CORE_POOLS=\"false\" ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=\"false\" STARSCAN=\"true\" PA_USE_BASE_TRACING=\"true\" ENABLE_PKEYS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:partition_alloc_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_PARTITION_ALLOC_AS_MALLOC_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=\"false\" ENABLE_DANGLING_RAW_PTR_CHECKS=\"false\" PUT_REF_COUNT_IN_PREVIOUS_SLOT=\"true\" ENABLE_GWP_ASAN_SUPPORT=\"true\" ENABLE_MTE_CHECKED_PTR_SUPPORT=\"false\" RECORD_ALLOC_INFO=\"false\" USE_FREESLOT_BITMAP=\"false\" GLUE_CORE_POOLS=\"false\" ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=\"false\" STARSCAN=\"true\" PA_USE_BASE_TRACING=\"true\" ENABLE_PKEYS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:partition_alloc_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/allocator/partition_allocator:partition_alloc_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_allocator_partition_allocator_partition_alloc_buildflags__host",
+    cmd: "echo '--flags ENABLE_PARTITION_ALLOC_AS_MALLOC_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SUPPORT=\"true\" ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=\"false\" ENABLE_DANGLING_RAW_PTR_CHECKS=\"false\" PUT_REF_COUNT_IN_PREVIOUS_SLOT=\"true\" ENABLE_GWP_ASAN_SUPPORT=\"true\" ENABLE_MTE_CHECKED_PTR_SUPPORT=\"false\" RECORD_ALLOC_INFO=\"false\" USE_FREESLOT_BITMAP=\"false\" GLUE_CORE_POOLS=\"false\" ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=\"false\" STARSCAN=\"true\" PA_USE_BASE_TRACING=\"true\" ENABLE_PKEYS=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base/allocator/partition_allocator:partition_alloc_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/allocator/partition_allocator/partition_alloc_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:anchor_functions_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_anchor_functions_buildflags__android_arm",
+    cmd: "echo '--flags USE_LLD=\"true\" SUPPORTS_CODE_ORDERING=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:anchor_functions_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/library_loader/anchor_functions_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:anchor_functions_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_anchor_functions_buildflags__android_arm64",
+    cmd: "echo '--flags USE_LLD=\"true\" SUPPORTS_CODE_ORDERING=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:anchor_functions_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/library_loader/anchor_functions_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:anchor_functions_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_anchor_functions_buildflags__android_x86",
+    cmd: "echo '--flags USE_LLD=\"true\" SUPPORTS_CODE_ORDERING=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:anchor_functions_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/library_loader/anchor_functions_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:anchor_functions_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_anchor_functions_buildflags__android_x86_64",
+    cmd: "echo '--flags USE_LLD=\"true\" SUPPORTS_CODE_ORDERING=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:anchor_functions_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/library_loader/anchor_functions_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:anchor_functions_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_anchor_functions_buildflags__host",
+    cmd: "echo '--flags USE_LLD=\"true\" SUPPORTS_CODE_ORDERING=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:anchor_functions_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/android/library_loader/anchor_functions_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:android_runtime_jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_base_android_runtime_jni_headers__android_arm",
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/android_runtime_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--jar_file " +
+         "$(location third_party/android_sdk/public/platforms/android-33/android.jar) " +
+         "--output_name " +
+         "Runnable_jni.h " +
+         "--output_name " +
+         "Runtime_jni.h " +
+         "--input_file " +
+         "java/lang/Runnable.class " +
+         "--input_file " +
+         "java/lang/Runtime.class " +
+         "--javap " +
+         "$$(find out/.path -name javap)",
+    out: [
+        "base/android_runtime_jni_headers/Runnable_jni.h",
+        "base/android_runtime_jni_headers/Runtime_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+        "third_party/android_sdk/public/platforms/android-33/android.jar",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:android_runtime_jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_android_runtime_jni_headers__android_arm64",
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/android_runtime_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--jar_file " +
+         "$(location third_party/android_sdk/public/platforms/android-33/android.jar) " +
+         "--output_name " +
+         "Runnable_jni.h " +
+         "--output_name " +
+         "Runtime_jni.h " +
+         "--input_file " +
+         "java/lang/Runnable.class " +
+         "--input_file " +
+         "java/lang/Runtime.class " +
+         "--javap " +
+         "$$(find out/.path -name javap)",
+    out: [
+        "base/android_runtime_jni_headers/Runnable_jni.h",
+        "base/android_runtime_jni_headers/Runtime_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+        "third_party/android_sdk/public/platforms/android-33/android.jar",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:android_runtime_jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_base_android_runtime_jni_headers__android_x86",
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/android_runtime_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--jar_file " +
+         "$(location third_party/android_sdk/public/platforms/android-33/android.jar) " +
+         "--output_name " +
+         "Runnable_jni.h " +
+         "--output_name " +
+         "Runtime_jni.h " +
+         "--input_file " +
+         "java/lang/Runnable.class " +
+         "--input_file " +
+         "java/lang/Runtime.class " +
+         "--javap " +
+         "$$(find out/.path -name javap)",
+    out: [
+        "base/android_runtime_jni_headers/Runnable_jni.h",
+        "base/android_runtime_jni_headers/Runtime_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+        "third_party/android_sdk/public/platforms/android-33/android.jar",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:android_runtime_jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_android_runtime_jni_headers__android_x86_64",
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/android_runtime_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--jar_file " +
+         "$(location third_party/android_sdk/public/platforms/android-33/android.jar) " +
+         "--output_name " +
+         "Runnable_jni.h " +
+         "--output_name " +
+         "Runtime_jni.h " +
+         "--input_file " +
+         "java/lang/Runnable.class " +
+         "--input_file " +
+         "java/lang/Runtime.class " +
+         "--javap " +
+         "$$(find out/.path -name javap)",
+    out: [
+        "base/android_runtime_jni_headers/Runnable_jni.h",
+        "base/android_runtime_jni_headers/Runtime_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+        "third_party/android_sdk/public/platforms/android-33/android.jar",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:base
+cc_library_static {
+    name: "cronet_aml_base_base",
+    srcs: [
+        ":cronet_aml_third_party_abseil_cpp_absl_base_base",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_log_severity",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_malloc_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_raw_logging_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_spinlock_wait",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_strerror",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_throw_delegate",
+        ":cronet_aml_third_party_abseil_cpp_absl_container_hashtablez_sampler",
+        ":cronet_aml_third_party_abseil_cpp_absl_container_raw_hash_set",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_debugging_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_demangle_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_examine_stack",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_failure_signal_handler",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_stacktrace",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_symbolize",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_city",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_hash",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_low_level_hash",
+        ":cronet_aml_third_party_abseil_cpp_absl_numeric_int128",
+        ":cronet_aml_third_party_abseil_cpp_absl_profiling_exponential_biased",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_distributions",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_platform",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_pool_urbg",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes_impl",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_slow",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_seed_material",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_seed_gen_exception",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_seed_sequences",
+        ":cronet_aml_third_party_abseil_cpp_absl_status_status",
+        ":cronet_aml_third_party_abseil_cpp_absl_status_statusor",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cord",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cord_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_functions",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_handle",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_info",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_str_format_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_strings",
+        ":cronet_aml_third_party_abseil_cpp_absl_synchronization_graphcycles_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_synchronization_synchronization",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_civil_time",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_time_zone",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_time",
+        ":cronet_aml_third_party_abseil_cpp_absl_types_bad_optional_access",
+        ":cronet_aml_third_party_abseil_cpp_absl_types_bad_variant_access",
+        "base/allocator/allocator_check.cc",
+        "base/allocator/allocator_extension.cc",
+        "base/allocator/dispatcher/dispatcher.cc",
+        "base/allocator/dispatcher/internal/dispatch_data.cc",
+        "base/allocator/dispatcher/reentry_guard.cc",
+        "base/allocator/partition_allocator/shim/allocator_shim.cc",
+        "base/at_exit.cc",
+        "base/barrier_closure.cc",
+        "base/base64.cc",
+        "base/base64url.cc",
+        "base/base_paths.cc",
+        "base/big_endian.cc",
+        "base/build_time.cc",
+        "base/callback_list.cc",
+        "base/check.cc",
+        "base/check_is_test.cc",
+        "base/check_op.cc",
+        "base/command_line.cc",
+        "base/containers/flat_tree.cc",
+        "base/containers/intrusive_heap.cc",
+        "base/containers/linked_list.cc",
+        "base/cpu.cc",
+        "base/cpu_reduction_experiment.cc",
+        "base/debug/activity_analyzer.cc",
+        "base/debug/activity_tracker.cc",
+        "base/debug/alias.cc",
+        "base/debug/asan_invalid_access.cc",
+        "base/debug/buffered_dwarf_reader.cc",
+        "base/debug/crash_logging.cc",
+        "base/debug/debugger.cc",
+        "base/debug/debugger_posix.cc",
+        "base/debug/dump_without_crashing.cc",
+        "base/debug/dwarf_line_no.cc",
+        "base/debug/elf_reader.cc",
+        "base/debug/proc_maps_linux.cc",
+        "base/debug/profiler.cc",
+        "base/debug/stack_trace.cc",
+        "base/debug/task_trace.cc",
+        "base/environment.cc",
+        "base/feature_list.cc",
+        "base/features.cc",
+        "base/file_descriptor_posix.cc",
+        "base/file_descriptor_store.cc",
+        "base/files/file.cc",
+        "base/files/file_descriptor_watcher_posix.cc",
+        "base/files/file_enumerator.cc",
+        "base/files/file_enumerator_posix.cc",
+        "base/files/file_path.cc",
+        "base/files/file_path_watcher.cc",
+        "base/files/file_path_watcher_inotify.cc",
+        "base/files/file_posix.cc",
+        "base/files/file_proxy.cc",
+        "base/files/file_tracing.cc",
+        "base/files/file_util.cc",
+        "base/files/file_util_posix.cc",
+        "base/files/important_file_writer.cc",
+        "base/files/important_file_writer_cleaner.cc",
+        "base/files/memory_mapped_file.cc",
+        "base/files/memory_mapped_file_posix.cc",
+        "base/files/safe_base_name.cc",
+        "base/files/scoped_file.cc",
+        "base/files/scoped_temp_dir.cc",
+        "base/functional/callback_helpers.cc",
+        "base/functional/callback_internal.cc",
+        "base/guid.cc",
+        "base/hash/hash.cc",
+        "base/hash/legacy_hash.cc",
+        "base/hash/md5_boringssl.cc",
+        "base/hash/sha1_boringssl.cc",
+        "base/json/json_file_value_serializer.cc",
+        "base/json/json_parser.cc",
+        "base/json/json_reader.cc",
+        "base/json/json_string_value_serializer.cc",
+        "base/json/json_value_converter.cc",
+        "base/json/json_writer.cc",
+        "base/json/string_escape.cc",
+        "base/json/values_util.cc",
+        "base/lazy_instance_helpers.cc",
+        "base/linux_util.cc",
+        "base/location.cc",
+        "base/logging.cc",
+        "base/memory/aligned_memory.cc",
+        "base/memory/discardable_memory.cc",
+        "base/memory/discardable_memory_allocator.cc",
+        "base/memory/discardable_shared_memory.cc",
+        "base/memory/madv_free_discardable_memory_allocator_posix.cc",
+        "base/memory/madv_free_discardable_memory_posix.cc",
+        "base/memory/memory_pressure_listener.cc",
+        "base/memory/memory_pressure_monitor.cc",
+        "base/memory/nonscannable_memory.cc",
+        "base/memory/page_size_posix.cc",
+        "base/memory/platform_shared_memory_handle.cc",
+        "base/memory/platform_shared_memory_region.cc",
+        "base/memory/raw_ptr.cc",
+        "base/memory/raw_ptr_asan_bound_arg_tracker.cc",
+        "base/memory/raw_ptr_asan_service.cc",
+        "base/memory/read_only_shared_memory_region.cc",
+        "base/memory/ref_counted.cc",
+        "base/memory/ref_counted_memory.cc",
+        "base/memory/shared_memory_mapper.cc",
+        "base/memory/shared_memory_mapping.cc",
+        "base/memory/shared_memory_security_policy.cc",
+        "base/memory/shared_memory_tracker.cc",
+        "base/memory/unsafe_shared_memory_pool.cc",
+        "base/memory/unsafe_shared_memory_region.cc",
+        "base/memory/weak_ptr.cc",
+        "base/memory/writable_shared_memory_region.cc",
+        "base/message_loop/message_pump.cc",
+        "base/message_loop/message_pump_default.cc",
+        "base/message_loop/message_pump_epoll.cc",
+        "base/message_loop/message_pump_libevent.cc",
+        "base/message_loop/watchable_io_message_pump_posix.cc",
+        "base/message_loop/work_id_provider.cc",
+        "base/metrics/bucket_ranges.cc",
+        "base/metrics/crc32.cc",
+        "base/metrics/dummy_histogram.cc",
+        "base/metrics/field_trial.cc",
+        "base/metrics/field_trial_param_associator.cc",
+        "base/metrics/field_trial_params.cc",
+        "base/metrics/histogram.cc",
+        "base/metrics/histogram_base.cc",
+        "base/metrics/histogram_delta_serialization.cc",
+        "base/metrics/histogram_functions.cc",
+        "base/metrics/histogram_samples.cc",
+        "base/metrics/histogram_snapshot_manager.cc",
+        "base/metrics/metrics_hashes.cc",
+        "base/metrics/persistent_histogram_allocator.cc",
+        "base/metrics/persistent_histogram_storage.cc",
+        "base/metrics/persistent_memory_allocator.cc",
+        "base/metrics/persistent_sample_map.cc",
+        "base/metrics/ranges_manager.cc",
+        "base/metrics/sample_map.cc",
+        "base/metrics/sample_vector.cc",
+        "base/metrics/single_sample_metrics.cc",
+        "base/metrics/sparse_histogram.cc",
+        "base/metrics/statistics_recorder.cc",
+        "base/metrics/user_metrics.cc",
+        "base/native_library.cc",
+        "base/native_library_posix.cc",
+        "base/observer_list_internal.cc",
+        "base/observer_list_threadsafe.cc",
+        "base/observer_list_types.cc",
+        "base/one_shot_event.cc",
+        "base/path_service.cc",
+        "base/pending_task.cc",
+        "base/pickle.cc",
+        "base/posix/can_lower_nice_to.cc",
+        "base/posix/file_descriptor_shuffle.cc",
+        "base/posix/global_descriptors.cc",
+        "base/posix/safe_strerror.cc",
+        "base/posix/unix_domain_socket.cc",
+        "base/power_monitor/battery_level_provider.cc",
+        "base/power_monitor/battery_state_sampler.cc",
+        "base/power_monitor/moving_average.cc",
+        "base/power_monitor/power_monitor.cc",
+        "base/power_monitor/power_monitor_device_source.cc",
+        "base/power_monitor/power_monitor_features.cc",
+        "base/power_monitor/power_monitor_source.cc",
+        "base/power_monitor/sampling_event_source.cc",
+        "base/power_monitor/timer_sampling_event_source.cc",
+        "base/process/environment_internal.cc",
+        "base/process/internal_linux.cc",
+        "base/process/kill.cc",
+        "base/process/kill_posix.cc",
+        "base/process/launch.cc",
+        "base/process/launch_posix.cc",
+        "base/process/memory.cc",
+        "base/process/memory_linux.cc",
+        "base/process/process_handle.cc",
+        "base/process/process_handle_linux.cc",
+        "base/process/process_handle_posix.cc",
+        "base/process/process_iterator.cc",
+        "base/process/process_iterator_linux.cc",
+        "base/process/process_metrics.cc",
+        "base/process/process_metrics_linux.cc",
+        "base/process/process_metrics_posix.cc",
+        "base/process/process_posix.cc",
+        "base/profiler/arm_cfi_table.cc",
+        "base/profiler/frame.cc",
+        "base/profiler/metadata_recorder.cc",
+        "base/profiler/module_cache.cc",
+        "base/profiler/module_cache_posix.cc",
+        "base/profiler/sample_metadata.cc",
+        "base/profiler/sampling_profiler_thread_token.cc",
+        "base/profiler/stack_base_address_posix.cc",
+        "base/profiler/stack_buffer.cc",
+        "base/profiler/stack_copier.cc",
+        "base/profiler/stack_copier_signal.cc",
+        "base/profiler/stack_copier_suspend.cc",
+        "base/profiler/stack_sampler.cc",
+        "base/profiler/stack_sampler_impl.cc",
+        "base/profiler/stack_sampling_profiler.cc",
+        "base/profiler/thread_delegate_posix.cc",
+        "base/profiler/unwinder.cc",
+        "base/rand_util.cc",
+        "base/rand_util_posix.cc",
+        "base/run_loop.cc",
+        "base/sampling_heap_profiler/lock_free_address_hash_set.cc",
+        "base/sampling_heap_profiler/poisson_allocation_sampler.cc",
+        "base/sampling_heap_profiler/sampling_heap_profiler.cc",
+        "base/scoped_add_feature_flags.cc",
+        "base/scoped_environment_variable_override.cc",
+        "base/scoped_native_library.cc",
+        "base/sequence_checker.cc",
+        "base/sequence_checker_impl.cc",
+        "base/sequence_token.cc",
+        "base/strings/abseil_string_conversions.cc",
+        "base/strings/abseil_string_number_conversions.cc",
+        "base/strings/escape.cc",
+        "base/strings/latin1_string_conversions.cc",
+        "base/strings/pattern.cc",
+        "base/strings/safe_sprintf.cc",
+        "base/strings/strcat.cc",
+        "base/strings/string_number_conversions.cc",
+        "base/strings/string_piece.cc",
+        "base/strings/string_split.cc",
+        "base/strings/string_util.cc",
+        "base/strings/string_util_constants.cc",
+        "base/strings/stringprintf.cc",
+        "base/strings/sys_string_conversions_posix.cc",
+        "base/strings/utf_offset_string_conversions.cc",
+        "base/strings/utf_string_conversion_utils.cc",
+        "base/strings/utf_string_conversions.cc",
+        "base/substring_set_matcher/matcher_string_pattern.cc",
+        "base/substring_set_matcher/substring_set_matcher.cc",
+        "base/supports_user_data.cc",
+        "base/sync_socket.cc",
+        "base/sync_socket_posix.cc",
+        "base/synchronization/atomic_flag.cc",
+        "base/synchronization/condition_variable_posix.cc",
+        "base/synchronization/lock.cc",
+        "base/synchronization/lock_impl_posix.cc",
+        "base/synchronization/waitable_event_posix.cc",
+        "base/synchronization/waitable_event_watcher_posix.cc",
+        "base/syslog_logging.cc",
+        "base/system/sys_info.cc",
+        "base/system/sys_info_linux.cc",
+        "base/system/sys_info_posix.cc",
+        "base/system/system_monitor.cc",
+        "base/task/cancelable_task_tracker.cc",
+        "base/task/common/checked_lock_impl.cc",
+        "base/task/common/lazy_now.cc",
+        "base/task/common/operations_controller.cc",
+        "base/task/common/scoped_defer_task_posting.cc",
+        "base/task/common/task_annotator.cc",
+        "base/task/current_thread.cc",
+        "base/task/default_delayed_task_handle_delegate.cc",
+        "base/task/deferred_sequenced_task_runner.cc",
+        "base/task/delayed_task_handle.cc",
+        "base/task/lazy_thread_pool_task_runner.cc",
+        "base/task/post_job.cc",
+        "base/task/scoped_set_task_priority_for_current_thread.cc",
+        "base/task/sequence_manager/associated_thread_id.cc",
+        "base/task/sequence_manager/atomic_flag_set.cc",
+        "base/task/sequence_manager/delayed_task_handle_delegate.cc",
+        "base/task/sequence_manager/enqueue_order_generator.cc",
+        "base/task/sequence_manager/fence.cc",
+        "base/task/sequence_manager/hierarchical_timing_wheel.cc",
+        "base/task/sequence_manager/sequence_manager.cc",
+        "base/task/sequence_manager/sequence_manager_impl.cc",
+        "base/task/sequence_manager/sequenced_task_source.cc",
+        "base/task/sequence_manager/task_order.cc",
+        "base/task/sequence_manager/task_queue.cc",
+        "base/task/sequence_manager/task_queue_impl.cc",
+        "base/task/sequence_manager/task_queue_selector.cc",
+        "base/task/sequence_manager/tasks.cc",
+        "base/task/sequence_manager/thread_controller.cc",
+        "base/task/sequence_manager/thread_controller_impl.cc",
+        "base/task/sequence_manager/thread_controller_power_monitor.cc",
+        "base/task/sequence_manager/thread_controller_with_message_pump_impl.cc",
+        "base/task/sequence_manager/time_domain.cc",
+        "base/task/sequence_manager/timing_wheel.cc",
+        "base/task/sequence_manager/wake_up_queue.cc",
+        "base/task/sequence_manager/work_deduplicator.cc",
+        "base/task/sequence_manager/work_queue.cc",
+        "base/task/sequence_manager/work_queue_sets.cc",
+        "base/task/sequenced_task_runner.cc",
+        "base/task/simple_task_executor.cc",
+        "base/task/single_thread_task_executor.cc",
+        "base/task/single_thread_task_runner.cc",
+        "base/task/task_executor.cc",
+        "base/task/task_features.cc",
+        "base/task/task_runner.cc",
+        "base/task/task_traits.cc",
+        "base/task/thread_pool.cc",
+        "base/task/thread_pool/delayed_priority_queue.cc",
+        "base/task/thread_pool/delayed_task_manager.cc",
+        "base/task/thread_pool/environment_config.cc",
+        "base/task/thread_pool/initialization_util.cc",
+        "base/task/thread_pool/job_task_source.cc",
+        "base/task/thread_pool/pooled_parallel_task_runner.cc",
+        "base/task/thread_pool/pooled_sequenced_task_runner.cc",
+        "base/task/thread_pool/pooled_single_thread_task_runner_manager.cc",
+        "base/task/thread_pool/pooled_task_runner_delegate.cc",
+        "base/task/thread_pool/priority_queue.cc",
+        "base/task/thread_pool/sequence.cc",
+        "base/task/thread_pool/service_thread.cc",
+        "base/task/thread_pool/task.cc",
+        "base/task/thread_pool/task_source.cc",
+        "base/task/thread_pool/task_source_sort_key.cc",
+        "base/task/thread_pool/task_tracker.cc",
+        "base/task/thread_pool/thread_group.cc",
+        "base/task/thread_pool/thread_group_impl.cc",
+        "base/task/thread_pool/thread_group_native.cc",
+        "base/task/thread_pool/thread_pool_impl.cc",
+        "base/task/thread_pool/thread_pool_instance.cc",
+        "base/task/thread_pool/worker_thread.cc",
+        "base/task/thread_pool/worker_thread_stack.cc",
+        "base/third_party/cityhash/city.cc",
+        "base/third_party/cityhash_v103/src/city_v103.cc",
+        "base/third_party/nspr/prtime.cc",
+        "base/third_party/superfasthash/superfasthash.c",
+        "base/threading/hang_watcher.cc",
+        "base/threading/platform_thread.cc",
+        "base/threading/platform_thread_internal_posix.cc",
+        "base/threading/platform_thread_posix.cc",
+        "base/threading/platform_thread_ref.cc",
+        "base/threading/post_task_and_reply_impl.cc",
+        "base/threading/scoped_blocking_call.cc",
+        "base/threading/scoped_blocking_call_internal.cc",
+        "base/threading/scoped_thread_priority.cc",
+        "base/threading/sequence_local_storage_map.cc",
+        "base/threading/sequence_local_storage_slot.cc",
+        "base/threading/sequenced_task_runner_handle.cc",
+        "base/threading/simple_thread.cc",
+        "base/threading/thread.cc",
+        "base/threading/thread_checker.cc",
+        "base/threading/thread_checker_impl.cc",
+        "base/threading/thread_collision_warner.cc",
+        "base/threading/thread_id_name_manager.cc",
+        "base/threading/thread_local_storage.cc",
+        "base/threading/thread_local_storage_posix.cc",
+        "base/threading/thread_restrictions.cc",
+        "base/threading/thread_task_runner_handle.cc",
+        "base/threading/watchdog.cc",
+        "base/time/clock.cc",
+        "base/time/default_clock.cc",
+        "base/time/default_tick_clock.cc",
+        "base/time/tick_clock.cc",
+        "base/time/time.cc",
+        "base/time/time_conversion_posix.cc",
+        "base/time/time_delta_from_string.cc",
+        "base/time/time_exploded_icu.cc",
+        "base/time/time_exploded_posix.cc",
+        "base/time/time_now_posix.cc",
+        "base/time/time_override.cc",
+        "base/time/time_to_iso8601.cc",
+        "base/timer/elapsed_timer.cc",
+        "base/timer/hi_res_timer_manager_posix.cc",
+        "base/timer/lap_timer.cc",
+        "base/timer/timer.cc",
+        "base/timer/wall_clock_timer.cc",
+        "base/token.cc",
+        "base/trace_event/heap_profiler_allocation_context.cc",
+        "base/trace_event/heap_profiler_allocation_context_tracker.cc",
+        "base/trace_event/memory_allocator_dump_guid.cc",
+        "base/trace_event/trace_event_stub.cc",
+        "base/trace_event/trace_id_helper.cc",
+        "base/unguessable_token.cc",
+        "base/value_iterators.cc",
+        "base/values.cc",
+        "base/version.cc",
+        "base/vlog.cc",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DBASE_IMPLEMENTATION",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+        "-DUSE_CHROMIUM_ICU=1",
+        "-DU_ENABLE_DYLOAD=0",
+        "-DU_ENABLE_RESOURCE_TRACING=0",
+        "-DU_ENABLE_TRACING=1",
+        "-DU_STATIC_IMPLEMENTATION",
+        "-DU_USING_ICU_NAMESPACE=0",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/icu/source/common/",
+        "third_party/icu/source/i18n/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android: {
+            shared_libs: [
+                "libandroid",
+                "liblog",
+            ],
+        },
+        android_arm: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_ashmem_ashmem",
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
+                "base/android/android_hardware_buffer_compat.cc",
+                "base/android/android_image_reader_compat.cc",
+                "base/android/apk_assets.cc",
+                "base/android/application_status_listener.cc",
+                "base/android/base_feature_list.cc",
+                "base/android/base_features.cc",
+                "base/android/base_jni_onload.cc",
+                "base/android/build_info.cc",
+                "base/android/bundle_utils.cc",
+                "base/android/callback_android.cc",
+                "base/android/child_process_service.cc",
+                "base/android/command_line_android.cc",
+                "base/android/content_uri_utils.cc",
+                "base/android/cpu_features.cc",
+                "base/android/early_trace_event_binding.cc",
+                "base/android/event_log.cc",
+                "base/android/feature_list_jni.cc",
+                "base/android/features_jni.cc",
+                "base/android/field_trial_list.cc",
+                "base/android/important_file_writer_android.cc",
+                "base/android/int_string_callback.cc",
+                "base/android/jank_metric_uma_recorder.cc",
+                "base/android/java_exception_reporter.cc",
+                "base/android/java_handler_thread.cc",
+                "base/android/java_heap_dump_generator.cc",
+                "base/android/java_runtime.cc",
+                "base/android/jni_android.cc",
+                "base/android/jni_array.cc",
+                "base/android/jni_registrar.cc",
+                "base/android/jni_string.cc",
+                "base/android/jni_utils.cc",
+                "base/android/jni_weak_ref.cc",
+                "base/android/library_loader/anchor_functions.cc",
+                "base/android/library_loader/library_loader_hooks.cc",
+                "base/android/library_loader/library_prefetcher.cc",
+                "base/android/library_loader/library_prefetcher_hooks.cc",
+                "base/android/locale_utils.cc",
+                "base/android/memory_pressure_listener_android.cc",
+                "base/android/native_uma_recorder.cc",
+                "base/android/path_service_android.cc",
+                "base/android/path_utils.cc",
+                "base/android/radio_utils.cc",
+                "base/android/reached_addresses_bitset.cc",
+                "base/android/reached_code_profiler.cc",
+                "base/android/remove_stale_data.cc",
+                "base/android/scoped_hardware_buffer_fence_sync.cc",
+                "base/android/scoped_hardware_buffer_handle.cc",
+                "base/android/scoped_java_ref.cc",
+                "base/android/statistics_recorder_android.cc",
+                "base/android/sys_utils.cc",
+                "base/android/task_scheduler/post_task_android.cc",
+                "base/android/task_scheduler/task_runner_android.cc",
+                "base/android/thread_instruction_count.cc",
+                "base/android/timezone_utils.cc",
+                "base/android/trace_event_binding.cc",
+                "base/android/unguessable_token_android.cc",
+                "base/base_paths_android.cc",
+                "base/debug/stack_trace_android.cc",
+                "base/files/file_util_android.cc",
+                "base/files/scoped_file_android.cc",
+                "base/memory/platform_shared_memory_mapper_android.cc",
+                "base/memory/platform_shared_memory_region_android.cc",
+                "base/message_loop/message_pump_android.cc",
+                "base/os_compat_android.cc",
+                "base/power_monitor/power_monitor_device_source_android.cc",
+                "base/process/process_android.cc",
+                "base/profiler/chrome_unwind_info_android.cc",
+                "base/profiler/chrome_unwinder_android.cc",
+                "base/profiler/chrome_unwinder_android_v2.cc",
+                "base/profiler/stack_sampler_android.cc",
+                "base/system/sys_info_android.cc",
+                "base/threading/platform_thread_android.cc",
+                "base/time/time_android.cc",
+                "base/trace_event/cfi_backtrace_android.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_arm",
+                "cronet_aml_base_anchor_functions_buildflags__android_arm",
+                "cronet_aml_base_android_runtime_jni_headers__android_arm",
+                "cronet_aml_base_base_jni_headers__android_arm",
+                "cronet_aml_base_build_date__android_arm",
+                "cronet_aml_base_cfi_buildflags__android_arm",
+                "cronet_aml_base_clang_profiling_buildflags__android_arm",
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_feature_list_buildflags__android_arm",
+                "cronet_aml_base_ios_cronet_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_base_message_pump_buildflags__android_arm",
+                "cronet_aml_base_orderfile_buildflags__android_arm",
+                "cronet_aml_base_parsing_buildflags__android_arm",
+                "cronet_aml_base_power_monitor_buildflags__android_arm",
+                "cronet_aml_base_profiler_buildflags__android_arm",
+                "cronet_aml_base_sanitizer_buildflags__android_arm",
+                "cronet_aml_base_synchronization_buildflags__android_arm",
+                "cronet_aml_base_tracing_buildflags__android_arm",
+                "cronet_aml_build_branding_buildflags__android_arm",
+                "cronet_aml_build_chromecast_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_arm",
+                "cronet_aml_base_anchor_functions_buildflags__android_arm",
+                "cronet_aml_base_android_runtime_jni_headers__android_arm",
+                "cronet_aml_base_base_jni_headers__android_arm",
+                "cronet_aml_base_build_date__android_arm",
+                "cronet_aml_base_cfi_buildflags__android_arm",
+                "cronet_aml_base_clang_profiling_buildflags__android_arm",
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_feature_list_buildflags__android_arm",
+                "cronet_aml_base_ios_cronet_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_base_message_pump_buildflags__android_arm",
+                "cronet_aml_base_orderfile_buildflags__android_arm",
+                "cronet_aml_base_parsing_buildflags__android_arm",
+                "cronet_aml_base_power_monitor_buildflags__android_arm",
+                "cronet_aml_base_profiler_buildflags__android_arm",
+                "cronet_aml_base_sanitizer_buildflags__android_arm",
+                "cronet_aml_base_synchronization_buildflags__android_arm",
+                "cronet_aml_base_tracing_buildflags__android_arm",
+                "cronet_aml_build_branding_buildflags__android_arm",
+                "cronet_aml_build_chromecast_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_ashmem_ashmem",
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
+                "base/android/android_hardware_buffer_compat.cc",
+                "base/android/android_image_reader_compat.cc",
+                "base/android/apk_assets.cc",
+                "base/android/application_status_listener.cc",
+                "base/android/base_feature_list.cc",
+                "base/android/base_features.cc",
+                "base/android/base_jni_onload.cc",
+                "base/android/build_info.cc",
+                "base/android/bundle_utils.cc",
+                "base/android/callback_android.cc",
+                "base/android/child_process_service.cc",
+                "base/android/command_line_android.cc",
+                "base/android/content_uri_utils.cc",
+                "base/android/cpu_features.cc",
+                "base/android/early_trace_event_binding.cc",
+                "base/android/event_log.cc",
+                "base/android/feature_list_jni.cc",
+                "base/android/features_jni.cc",
+                "base/android/field_trial_list.cc",
+                "base/android/important_file_writer_android.cc",
+                "base/android/int_string_callback.cc",
+                "base/android/jank_metric_uma_recorder.cc",
+                "base/android/java_exception_reporter.cc",
+                "base/android/java_handler_thread.cc",
+                "base/android/java_heap_dump_generator.cc",
+                "base/android/java_runtime.cc",
+                "base/android/jni_android.cc",
+                "base/android/jni_array.cc",
+                "base/android/jni_registrar.cc",
+                "base/android/jni_string.cc",
+                "base/android/jni_utils.cc",
+                "base/android/jni_weak_ref.cc",
+                "base/android/library_loader/anchor_functions.cc",
+                "base/android/library_loader/library_loader_hooks.cc",
+                "base/android/library_loader/library_prefetcher.cc",
+                "base/android/library_loader/library_prefetcher_hooks.cc",
+                "base/android/locale_utils.cc",
+                "base/android/memory_pressure_listener_android.cc",
+                "base/android/native_uma_recorder.cc",
+                "base/android/path_service_android.cc",
+                "base/android/path_utils.cc",
+                "base/android/radio_utils.cc",
+                "base/android/reached_addresses_bitset.cc",
+                "base/android/reached_code_profiler.cc",
+                "base/android/remove_stale_data.cc",
+                "base/android/scoped_hardware_buffer_fence_sync.cc",
+                "base/android/scoped_hardware_buffer_handle.cc",
+                "base/android/scoped_java_ref.cc",
+                "base/android/statistics_recorder_android.cc",
+                "base/android/sys_utils.cc",
+                "base/android/task_scheduler/post_task_android.cc",
+                "base/android/task_scheduler/task_runner_android.cc",
+                "base/android/thread_instruction_count.cc",
+                "base/android/timezone_utils.cc",
+                "base/android/trace_event_binding.cc",
+                "base/android/unguessable_token_android.cc",
+                "base/base_paths_android.cc",
+                "base/debug/stack_trace_android.cc",
+                "base/files/file_util_android.cc",
+                "base/files/scoped_file_android.cc",
+                "base/memory/platform_shared_memory_mapper_android.cc",
+                "base/memory/platform_shared_memory_region_android.cc",
+                "base/message_loop/message_pump_android.cc",
+                "base/os_compat_android.cc",
+                "base/power_monitor/power_monitor_device_source_android.cc",
+                "base/process/process_android.cc",
+                "base/profiler/stack_sampler_android.cc",
+                "base/system/sys_info_android.cc",
+                "base/threading/platform_thread_android.cc",
+                "base/time/time_android.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_arm64",
+                "cronet_aml_base_anchor_functions_buildflags__android_arm64",
+                "cronet_aml_base_android_runtime_jni_headers__android_arm64",
+                "cronet_aml_base_base_jni_headers__android_arm64",
+                "cronet_aml_base_build_date__android_arm64",
+                "cronet_aml_base_cfi_buildflags__android_arm64",
+                "cronet_aml_base_clang_profiling_buildflags__android_arm64",
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_feature_list_buildflags__android_arm64",
+                "cronet_aml_base_ios_cronet_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_base_message_pump_buildflags__android_arm64",
+                "cronet_aml_base_orderfile_buildflags__android_arm64",
+                "cronet_aml_base_parsing_buildflags__android_arm64",
+                "cronet_aml_base_power_monitor_buildflags__android_arm64",
+                "cronet_aml_base_profiler_buildflags__android_arm64",
+                "cronet_aml_base_sanitizer_buildflags__android_arm64",
+                "cronet_aml_base_synchronization_buildflags__android_arm64",
+                "cronet_aml_base_tracing_buildflags__android_arm64",
+                "cronet_aml_build_branding_buildflags__android_arm64",
+                "cronet_aml_build_chromecast_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_arm64",
+                "cronet_aml_base_anchor_functions_buildflags__android_arm64",
+                "cronet_aml_base_android_runtime_jni_headers__android_arm64",
+                "cronet_aml_base_base_jni_headers__android_arm64",
+                "cronet_aml_base_build_date__android_arm64",
+                "cronet_aml_base_cfi_buildflags__android_arm64",
+                "cronet_aml_base_clang_profiling_buildflags__android_arm64",
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_feature_list_buildflags__android_arm64",
+                "cronet_aml_base_ios_cronet_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_base_message_pump_buildflags__android_arm64",
+                "cronet_aml_base_orderfile_buildflags__android_arm64",
+                "cronet_aml_base_parsing_buildflags__android_arm64",
+                "cronet_aml_base_power_monitor_buildflags__android_arm64",
+                "cronet_aml_base_profiler_buildflags__android_arm64",
+                "cronet_aml_base_sanitizer_buildflags__android_arm64",
+                "cronet_aml_base_synchronization_buildflags__android_arm64",
+                "cronet_aml_base_tracing_buildflags__android_arm64",
+                "cronet_aml_build_branding_buildflags__android_arm64",
+                "cronet_aml_build_chromecast_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_ashmem_ashmem",
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
+                "base/android/android_hardware_buffer_compat.cc",
+                "base/android/android_image_reader_compat.cc",
+                "base/android/apk_assets.cc",
+                "base/android/application_status_listener.cc",
+                "base/android/base_feature_list.cc",
+                "base/android/base_features.cc",
+                "base/android/base_jni_onload.cc",
+                "base/android/build_info.cc",
+                "base/android/bundle_utils.cc",
+                "base/android/callback_android.cc",
+                "base/android/child_process_service.cc",
+                "base/android/command_line_android.cc",
+                "base/android/content_uri_utils.cc",
+                "base/android/cpu_features.cc",
+                "base/android/early_trace_event_binding.cc",
+                "base/android/event_log.cc",
+                "base/android/feature_list_jni.cc",
+                "base/android/features_jni.cc",
+                "base/android/field_trial_list.cc",
+                "base/android/important_file_writer_android.cc",
+                "base/android/int_string_callback.cc",
+                "base/android/jank_metric_uma_recorder.cc",
+                "base/android/java_exception_reporter.cc",
+                "base/android/java_handler_thread.cc",
+                "base/android/java_heap_dump_generator.cc",
+                "base/android/java_runtime.cc",
+                "base/android/jni_android.cc",
+                "base/android/jni_array.cc",
+                "base/android/jni_registrar.cc",
+                "base/android/jni_string.cc",
+                "base/android/jni_utils.cc",
+                "base/android/jni_weak_ref.cc",
+                "base/android/library_loader/anchor_functions.cc",
+                "base/android/library_loader/library_loader_hooks.cc",
+                "base/android/library_loader/library_prefetcher.cc",
+                "base/android/library_loader/library_prefetcher_hooks.cc",
+                "base/android/locale_utils.cc",
+                "base/android/memory_pressure_listener_android.cc",
+                "base/android/native_uma_recorder.cc",
+                "base/android/path_service_android.cc",
+                "base/android/path_utils.cc",
+                "base/android/radio_utils.cc",
+                "base/android/reached_addresses_bitset.cc",
+                "base/android/reached_code_profiler_stub.cc",
+                "base/android/remove_stale_data.cc",
+                "base/android/scoped_hardware_buffer_fence_sync.cc",
+                "base/android/scoped_hardware_buffer_handle.cc",
+                "base/android/scoped_java_ref.cc",
+                "base/android/statistics_recorder_android.cc",
+                "base/android/sys_utils.cc",
+                "base/android/task_scheduler/post_task_android.cc",
+                "base/android/task_scheduler/task_runner_android.cc",
+                "base/android/thread_instruction_count.cc",
+                "base/android/timezone_utils.cc",
+                "base/android/trace_event_binding.cc",
+                "base/android/unguessable_token_android.cc",
+                "base/base_paths_android.cc",
+                "base/debug/stack_trace_android.cc",
+                "base/files/file_util_android.cc",
+                "base/files/scoped_file_android.cc",
+                "base/memory/platform_shared_memory_mapper_android.cc",
+                "base/memory/platform_shared_memory_region_android.cc",
+                "base/message_loop/message_pump_android.cc",
+                "base/os_compat_android.cc",
+                "base/power_monitor/power_monitor_device_source_android.cc",
+                "base/process/process_android.cc",
+                "base/profiler/stack_sampler_android.cc",
+                "base/system/sys_info_android.cc",
+                "base/threading/platform_thread_android.cc",
+                "base/time/time_android.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_x86",
+                "cronet_aml_base_anchor_functions_buildflags__android_x86",
+                "cronet_aml_base_android_runtime_jni_headers__android_x86",
+                "cronet_aml_base_base_jni_headers__android_x86",
+                "cronet_aml_base_build_date__android_x86",
+                "cronet_aml_base_cfi_buildflags__android_x86",
+                "cronet_aml_base_clang_profiling_buildflags__android_x86",
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_feature_list_buildflags__android_x86",
+                "cronet_aml_base_ios_cronet_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_base_message_pump_buildflags__android_x86",
+                "cronet_aml_base_orderfile_buildflags__android_x86",
+                "cronet_aml_base_parsing_buildflags__android_x86",
+                "cronet_aml_base_power_monitor_buildflags__android_x86",
+                "cronet_aml_base_profiler_buildflags__android_x86",
+                "cronet_aml_base_sanitizer_buildflags__android_x86",
+                "cronet_aml_base_synchronization_buildflags__android_x86",
+                "cronet_aml_base_tracing_buildflags__android_x86",
+                "cronet_aml_build_branding_buildflags__android_x86",
+                "cronet_aml_build_chromecast_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_x86",
+                "cronet_aml_base_anchor_functions_buildflags__android_x86",
+                "cronet_aml_base_android_runtime_jni_headers__android_x86",
+                "cronet_aml_base_base_jni_headers__android_x86",
+                "cronet_aml_base_build_date__android_x86",
+                "cronet_aml_base_cfi_buildflags__android_x86",
+                "cronet_aml_base_clang_profiling_buildflags__android_x86",
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_feature_list_buildflags__android_x86",
+                "cronet_aml_base_ios_cronet_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_base_message_pump_buildflags__android_x86",
+                "cronet_aml_base_orderfile_buildflags__android_x86",
+                "cronet_aml_base_parsing_buildflags__android_x86",
+                "cronet_aml_base_power_monitor_buildflags__android_x86",
+                "cronet_aml_base_profiler_buildflags__android_x86",
+                "cronet_aml_base_sanitizer_buildflags__android_x86",
+                "cronet_aml_base_synchronization_buildflags__android_x86",
+                "cronet_aml_base_tracing_buildflags__android_x86",
+                "cronet_aml_build_branding_buildflags__android_x86",
+                "cronet_aml_build_chromecast_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_ashmem_ashmem",
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc",
+                "base/android/android_hardware_buffer_compat.cc",
+                "base/android/android_image_reader_compat.cc",
+                "base/android/apk_assets.cc",
+                "base/android/application_status_listener.cc",
+                "base/android/base_feature_list.cc",
+                "base/android/base_features.cc",
+                "base/android/base_jni_onload.cc",
+                "base/android/build_info.cc",
+                "base/android/bundle_utils.cc",
+                "base/android/callback_android.cc",
+                "base/android/child_process_service.cc",
+                "base/android/command_line_android.cc",
+                "base/android/content_uri_utils.cc",
+                "base/android/cpu_features.cc",
+                "base/android/early_trace_event_binding.cc",
+                "base/android/event_log.cc",
+                "base/android/feature_list_jni.cc",
+                "base/android/features_jni.cc",
+                "base/android/field_trial_list.cc",
+                "base/android/important_file_writer_android.cc",
+                "base/android/int_string_callback.cc",
+                "base/android/jank_metric_uma_recorder.cc",
+                "base/android/java_exception_reporter.cc",
+                "base/android/java_handler_thread.cc",
+                "base/android/java_heap_dump_generator.cc",
+                "base/android/java_runtime.cc",
+                "base/android/jni_android.cc",
+                "base/android/jni_array.cc",
+                "base/android/jni_registrar.cc",
+                "base/android/jni_string.cc",
+                "base/android/jni_utils.cc",
+                "base/android/jni_weak_ref.cc",
+                "base/android/library_loader/anchor_functions.cc",
+                "base/android/library_loader/library_loader_hooks.cc",
+                "base/android/library_loader/library_prefetcher.cc",
+                "base/android/library_loader/library_prefetcher_hooks.cc",
+                "base/android/locale_utils.cc",
+                "base/android/memory_pressure_listener_android.cc",
+                "base/android/native_uma_recorder.cc",
+                "base/android/path_service_android.cc",
+                "base/android/path_utils.cc",
+                "base/android/radio_utils.cc",
+                "base/android/reached_addresses_bitset.cc",
+                "base/android/reached_code_profiler_stub.cc",
+                "base/android/remove_stale_data.cc",
+                "base/android/scoped_hardware_buffer_fence_sync.cc",
+                "base/android/scoped_hardware_buffer_handle.cc",
+                "base/android/scoped_java_ref.cc",
+                "base/android/statistics_recorder_android.cc",
+                "base/android/sys_utils.cc",
+                "base/android/task_scheduler/post_task_android.cc",
+                "base/android/task_scheduler/task_runner_android.cc",
+                "base/android/thread_instruction_count.cc",
+                "base/android/timezone_utils.cc",
+                "base/android/trace_event_binding.cc",
+                "base/android/unguessable_token_android.cc",
+                "base/base_paths_android.cc",
+                "base/debug/stack_trace_android.cc",
+                "base/files/file_util_android.cc",
+                "base/files/scoped_file_android.cc",
+                "base/memory/platform_shared_memory_mapper_android.cc",
+                "base/memory/platform_shared_memory_region_android.cc",
+                "base/message_loop/message_pump_android.cc",
+                "base/os_compat_android.cc",
+                "base/power_monitor/power_monitor_device_source_android.cc",
+                "base/process/process_android.cc",
+                "base/profiler/stack_sampler_android.cc",
+                "base/system/sys_info_android.cc",
+                "base/threading/platform_thread_android.cc",
+                "base/time/time_android.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_x86_64",
+                "cronet_aml_base_anchor_functions_buildflags__android_x86_64",
+                "cronet_aml_base_android_runtime_jni_headers__android_x86_64",
+                "cronet_aml_base_base_jni_headers__android_x86_64",
+                "cronet_aml_base_build_date__android_x86_64",
+                "cronet_aml_base_cfi_buildflags__android_x86_64",
+                "cronet_aml_base_clang_profiling_buildflags__android_x86_64",
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_feature_list_buildflags__android_x86_64",
+                "cronet_aml_base_ios_cronet_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_base_message_pump_buildflags__android_x86_64",
+                "cronet_aml_base_orderfile_buildflags__android_x86_64",
+                "cronet_aml_base_parsing_buildflags__android_x86_64",
+                "cronet_aml_base_power_monitor_buildflags__android_x86_64",
+                "cronet_aml_base_profiler_buildflags__android_x86_64",
+                "cronet_aml_base_sanitizer_buildflags__android_x86_64",
+                "cronet_aml_base_synchronization_buildflags__android_x86_64",
+                "cronet_aml_base_tracing_buildflags__android_x86_64",
+                "cronet_aml_build_branding_buildflags__android_x86_64",
+                "cronet_aml_build_chromecast_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_buildflags__android_x86_64",
+                "cronet_aml_base_anchor_functions_buildflags__android_x86_64",
+                "cronet_aml_base_android_runtime_jni_headers__android_x86_64",
+                "cronet_aml_base_base_jni_headers__android_x86_64",
+                "cronet_aml_base_build_date__android_x86_64",
+                "cronet_aml_base_cfi_buildflags__android_x86_64",
+                "cronet_aml_base_clang_profiling_buildflags__android_x86_64",
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_feature_list_buildflags__android_x86_64",
+                "cronet_aml_base_ios_cronet_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_base_message_pump_buildflags__android_x86_64",
+                "cronet_aml_base_orderfile_buildflags__android_x86_64",
+                "cronet_aml_base_parsing_buildflags__android_x86_64",
+                "cronet_aml_base_power_monitor_buildflags__android_x86_64",
+                "cronet_aml_base_profiler_buildflags__android_x86_64",
+                "cronet_aml_base_sanitizer_buildflags__android_x86_64",
+                "cronet_aml_base_synchronization_buildflags__android_x86_64",
+                "cronet_aml_base_tracing_buildflags__android_x86_64",
+                "cronet_aml_build_branding_buildflags__android_x86_64",
+                "cronet_aml_build_chromecast_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_build_config_compiler_compiler_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            srcs: [
+                "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
+                "base/base_paths_posix.cc",
+                "base/debug/stack_trace_posix.cc",
+                "base/files/file_util_linux.cc",
+                "base/files/scoped_file_linux.cc",
+                "base/memory/platform_shared_memory_mapper_posix.cc",
+                "base/memory/platform_shared_memory_region_posix.cc",
+                "base/nix/mime_util_xdg.cc",
+                "base/nix/xdg_util.cc",
+                "base/power_monitor/power_monitor_device_source_stub.cc",
+                "base/process/process_linux.cc",
+                "base/profiler/stack_sampler_posix.cc",
+                "base/stack_canary_linux.cc",
+                "base/threading/platform_thread_linux.cc",
+            ],
+            static_libs: [
+                "cronet_aml_base_third_party_symbolize_symbolize",
+                "cronet_aml_base_third_party_xdg_mime_xdg_mime",
+                "cronet_aml_base_third_party_xdg_user_dirs_xdg_user_dirs",
+            ],
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DGLOG_EXPORT=",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_SYMBOLIZE",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_allocator_buildflags__host",
+                "cronet_aml_base_anchor_functions_buildflags__host",
+                "cronet_aml_base_build_date__host",
+                "cronet_aml_base_cfi_buildflags__host",
+                "cronet_aml_base_clang_profiling_buildflags__host",
+                "cronet_aml_base_debugging_buildflags__host",
+                "cronet_aml_base_feature_list_buildflags__host",
+                "cronet_aml_base_ios_cronet_buildflags__host",
+                "cronet_aml_base_logging_buildflags__host",
+                "cronet_aml_base_message_pump_buildflags__host",
+                "cronet_aml_base_orderfile_buildflags__host",
+                "cronet_aml_base_parsing_buildflags__host",
+                "cronet_aml_base_power_monitor_buildflags__host",
+                "cronet_aml_base_profiler_buildflags__host",
+                "cronet_aml_base_sanitizer_buildflags__host",
+                "cronet_aml_base_synchronization_buildflags__host",
+                "cronet_aml_base_tracing_buildflags__host",
+                "cronet_aml_build_branding_buildflags__host",
+                "cronet_aml_build_chromecast_buildflags__host",
+                "cronet_aml_build_chromeos_buildflags__host",
+                "cronet_aml_build_config_compiler_compiler_buildflags__host",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_allocator_buildflags__host",
+                "cronet_aml_base_anchor_functions_buildflags__host",
+                "cronet_aml_base_build_date__host",
+                "cronet_aml_base_cfi_buildflags__host",
+                "cronet_aml_base_clang_profiling_buildflags__host",
+                "cronet_aml_base_debugging_buildflags__host",
+                "cronet_aml_base_feature_list_buildflags__host",
+                "cronet_aml_base_ios_cronet_buildflags__host",
+                "cronet_aml_base_logging_buildflags__host",
+                "cronet_aml_base_message_pump_buildflags__host",
+                "cronet_aml_base_orderfile_buildflags__host",
+                "cronet_aml_base_parsing_buildflags__host",
+                "cronet_aml_base_power_monitor_buildflags__host",
+                "cronet_aml_base_profiler_buildflags__host",
+                "cronet_aml_base_sanitizer_buildflags__host",
+                "cronet_aml_base_synchronization_buildflags__host",
+                "cronet_aml_base_tracing_buildflags__host",
+                "cronet_aml_build_branding_buildflags__host",
+                "cronet_aml_build_chromecast_buildflags__host",
+                "cronet_aml_build_chromeos_buildflags__host",
+                "cronet_aml_build_config_compiler_compiler_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //base:base_android_java_enums_srcjar
+java_genrule {
+    name: "cronet_aml_base_base_android_java_enums_srcjar",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location base/android/application_status_listener.h) " +
+         "$(location base/android/child_process_binding_types.h) " +
+         "$(location base/android/library_loader/library_loader_hooks.h) " +
+         "$(location base/android/linker/modern_linker_jni.h) " +
+         "$(location base/android/task_scheduler/task_runner_android.h) " +
+         "$(location base/memory/memory_pressure_listener.h) " +
+         "$(location base/metrics/histogram_base.h) " +
+         "$(location base/task/task_traits.h)",
+    out: [
+        "base/base_android_java_enums_srcjar.srcjar",
+    ],
+    tool_files: [
+        "base/android/application_status_listener.h",
+        "base/android/child_process_binding_types.h",
+        "base/android/library_loader/library_loader_hooks.h",
+        "base/android/linker/modern_linker_jni.h",
+        "base/android/task_scheduler/task_runner_android.h",
+        "base/memory/memory_pressure_listener.h",
+        "base/metrics/histogram_base.h",
+        "base/task/task_traits.h",
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+    ],
+}
+
+// GN: //base:base_jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_base_base_jni_headers__android_arm",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/SysUtils.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/base_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "ApkAssets_jni.h " +
+         "--output_name " +
+         "ApplicationStatus_jni.h " +
+         "--output_name " +
+         "BaseFeatureList_jni.h " +
+         "--output_name " +
+         "BuildInfo_jni.h " +
+         "--output_name " +
+         "BundleUtils_jni.h " +
+         "--output_name " +
+         "Callback_jni.h " +
+         "--output_name " +
+         "CommandLine_jni.h " +
+         "--output_name " +
+         "ContentUriUtils_jni.h " +
+         "--output_name " +
+         "CpuFeatures_jni.h " +
+         "--output_name " +
+         "EarlyTraceEvent_jni.h " +
+         "--output_name " +
+         "EventLog_jni.h " +
+         "--output_name " +
+         "FeatureList_jni.h " +
+         "--output_name " +
+         "Features_jni.h " +
+         "--output_name " +
+         "FieldTrialList_jni.h " +
+         "--output_name " +
+         "FileUtils_jni.h " +
+         "--output_name " +
+         "ImportantFileWriterAndroid_jni.h " +
+         "--output_name " +
+         "IntStringCallback_jni.h " +
+         "--output_name " +
+         "JNIUtils_jni.h " +
+         "--output_name " +
+         "JavaExceptionReporter_jni.h " +
+         "--output_name " +
+         "JavaHandlerThread_jni.h " +
+         "--output_name " +
+         "LocaleUtils_jni.h " +
+         "--output_name " +
+         "MemoryPressureListener_jni.h " +
+         "--output_name " +
+         "PathService_jni.h " +
+         "--output_name " +
+         "PathUtils_jni.h " +
+         "--output_name " +
+         "PowerMonitor_jni.h " +
+         "--output_name " +
+         "RadioUtils_jni.h " +
+         "--output_name " +
+         "SysUtils_jni.h " +
+         "--output_name " +
+         "ThreadUtils_jni.h " +
+         "--output_name " +
+         "TimezoneUtils_jni.h " +
+         "--output_name " +
+         "TraceEvent_jni.h " +
+         "--output_name " +
+         "UnguessableToken_jni.h " +
+         "--output_name " +
+         "JankMetricUMARecorder_jni.h " +
+         "--output_name " +
+         "LibraryLoader_jni.h " +
+         "--output_name " +
+         "LibraryPrefetcher_jni.h " +
+         "--output_name " +
+         "JavaHeapDumpGenerator_jni.h " +
+         "--output_name " +
+         "NativeUmaRecorder_jni.h " +
+         "--output_name " +
+         "StatisticsRecorderAndroid_jni.h " +
+         "--output_name " +
+         "ChildProcessService_jni.h " +
+         "--output_name " +
+         "PostTask_jni.h " +
+         "--output_name " +
+         "TaskRunnerImpl_jni.h " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApkAssets.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApplicationStatus.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BaseFeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BuildInfo.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BundleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Callback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CommandLine.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ContentUriUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CpuFeatures.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EarlyTraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EventLog.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Features.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FieldTrialList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FileUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/IntStringCallback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JNIUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaExceptionReporter.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaHandlerThread.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/LocaleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/MemoryPressureListener.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PowerMonitor.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/RadioUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/SysUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ThreadUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TimezoneUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/UnguessableToken.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/PostTask.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java)",
+    out: [
+        "base/base_jni_headers/ApkAssets_jni.h",
+        "base/base_jni_headers/ApplicationStatus_jni.h",
+        "base/base_jni_headers/BaseFeatureList_jni.h",
+        "base/base_jni_headers/BuildInfo_jni.h",
+        "base/base_jni_headers/BundleUtils_jni.h",
+        "base/base_jni_headers/Callback_jni.h",
+        "base/base_jni_headers/ChildProcessService_jni.h",
+        "base/base_jni_headers/CommandLine_jni.h",
+        "base/base_jni_headers/ContentUriUtils_jni.h",
+        "base/base_jni_headers/CpuFeatures_jni.h",
+        "base/base_jni_headers/EarlyTraceEvent_jni.h",
+        "base/base_jni_headers/EventLog_jni.h",
+        "base/base_jni_headers/FeatureList_jni.h",
+        "base/base_jni_headers/Features_jni.h",
+        "base/base_jni_headers/FieldTrialList_jni.h",
+        "base/base_jni_headers/FileUtils_jni.h",
+        "base/base_jni_headers/ImportantFileWriterAndroid_jni.h",
+        "base/base_jni_headers/IntStringCallback_jni.h",
+        "base/base_jni_headers/JNIUtils_jni.h",
+        "base/base_jni_headers/JankMetricUMARecorder_jni.h",
+        "base/base_jni_headers/JavaExceptionReporter_jni.h",
+        "base/base_jni_headers/JavaHandlerThread_jni.h",
+        "base/base_jni_headers/JavaHeapDumpGenerator_jni.h",
+        "base/base_jni_headers/LibraryLoader_jni.h",
+        "base/base_jni_headers/LibraryPrefetcher_jni.h",
+        "base/base_jni_headers/LocaleUtils_jni.h",
+        "base/base_jni_headers/MemoryPressureListener_jni.h",
+        "base/base_jni_headers/NativeUmaRecorder_jni.h",
+        "base/base_jni_headers/PathService_jni.h",
+        "base/base_jni_headers/PathUtils_jni.h",
+        "base/base_jni_headers/PostTask_jni.h",
+        "base/base_jni_headers/PowerMonitor_jni.h",
+        "base/base_jni_headers/RadioUtils_jni.h",
+        "base/base_jni_headers/StatisticsRecorderAndroid_jni.h",
+        "base/base_jni_headers/SysUtils_jni.h",
+        "base/base_jni_headers/TaskRunnerImpl_jni.h",
+        "base/base_jni_headers/ThreadUtils_jni.h",
+        "base/base_jni_headers/TimezoneUtils_jni.h",
+        "base/base_jni_headers/TraceEvent_jni.h",
+        "base/base_jni_headers/UnguessableToken_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:base_jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_base_jni_headers__android_arm64",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/SysUtils.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/base_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "ApkAssets_jni.h " +
+         "--output_name " +
+         "ApplicationStatus_jni.h " +
+         "--output_name " +
+         "BaseFeatureList_jni.h " +
+         "--output_name " +
+         "BuildInfo_jni.h " +
+         "--output_name " +
+         "BundleUtils_jni.h " +
+         "--output_name " +
+         "Callback_jni.h " +
+         "--output_name " +
+         "CommandLine_jni.h " +
+         "--output_name " +
+         "ContentUriUtils_jni.h " +
+         "--output_name " +
+         "CpuFeatures_jni.h " +
+         "--output_name " +
+         "EarlyTraceEvent_jni.h " +
+         "--output_name " +
+         "EventLog_jni.h " +
+         "--output_name " +
+         "FeatureList_jni.h " +
+         "--output_name " +
+         "Features_jni.h " +
+         "--output_name " +
+         "FieldTrialList_jni.h " +
+         "--output_name " +
+         "FileUtils_jni.h " +
+         "--output_name " +
+         "ImportantFileWriterAndroid_jni.h " +
+         "--output_name " +
+         "IntStringCallback_jni.h " +
+         "--output_name " +
+         "JNIUtils_jni.h " +
+         "--output_name " +
+         "JavaExceptionReporter_jni.h " +
+         "--output_name " +
+         "JavaHandlerThread_jni.h " +
+         "--output_name " +
+         "LocaleUtils_jni.h " +
+         "--output_name " +
+         "MemoryPressureListener_jni.h " +
+         "--output_name " +
+         "PathService_jni.h " +
+         "--output_name " +
+         "PathUtils_jni.h " +
+         "--output_name " +
+         "PowerMonitor_jni.h " +
+         "--output_name " +
+         "RadioUtils_jni.h " +
+         "--output_name " +
+         "SysUtils_jni.h " +
+         "--output_name " +
+         "ThreadUtils_jni.h " +
+         "--output_name " +
+         "TimezoneUtils_jni.h " +
+         "--output_name " +
+         "TraceEvent_jni.h " +
+         "--output_name " +
+         "UnguessableToken_jni.h " +
+         "--output_name " +
+         "JankMetricUMARecorder_jni.h " +
+         "--output_name " +
+         "LibraryLoader_jni.h " +
+         "--output_name " +
+         "LibraryPrefetcher_jni.h " +
+         "--output_name " +
+         "JavaHeapDumpGenerator_jni.h " +
+         "--output_name " +
+         "NativeUmaRecorder_jni.h " +
+         "--output_name " +
+         "StatisticsRecorderAndroid_jni.h " +
+         "--output_name " +
+         "ChildProcessService_jni.h " +
+         "--output_name " +
+         "PostTask_jni.h " +
+         "--output_name " +
+         "TaskRunnerImpl_jni.h " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApkAssets.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApplicationStatus.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BaseFeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BuildInfo.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BundleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Callback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CommandLine.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ContentUriUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CpuFeatures.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EarlyTraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EventLog.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Features.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FieldTrialList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FileUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/IntStringCallback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JNIUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaExceptionReporter.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaHandlerThread.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/LocaleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/MemoryPressureListener.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PowerMonitor.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/RadioUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/SysUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ThreadUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TimezoneUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/UnguessableToken.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/PostTask.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java)",
+    out: [
+        "base/base_jni_headers/ApkAssets_jni.h",
+        "base/base_jni_headers/ApplicationStatus_jni.h",
+        "base/base_jni_headers/BaseFeatureList_jni.h",
+        "base/base_jni_headers/BuildInfo_jni.h",
+        "base/base_jni_headers/BundleUtils_jni.h",
+        "base/base_jni_headers/Callback_jni.h",
+        "base/base_jni_headers/ChildProcessService_jni.h",
+        "base/base_jni_headers/CommandLine_jni.h",
+        "base/base_jni_headers/ContentUriUtils_jni.h",
+        "base/base_jni_headers/CpuFeatures_jni.h",
+        "base/base_jni_headers/EarlyTraceEvent_jni.h",
+        "base/base_jni_headers/EventLog_jni.h",
+        "base/base_jni_headers/FeatureList_jni.h",
+        "base/base_jni_headers/Features_jni.h",
+        "base/base_jni_headers/FieldTrialList_jni.h",
+        "base/base_jni_headers/FileUtils_jni.h",
+        "base/base_jni_headers/ImportantFileWriterAndroid_jni.h",
+        "base/base_jni_headers/IntStringCallback_jni.h",
+        "base/base_jni_headers/JNIUtils_jni.h",
+        "base/base_jni_headers/JankMetricUMARecorder_jni.h",
+        "base/base_jni_headers/JavaExceptionReporter_jni.h",
+        "base/base_jni_headers/JavaHandlerThread_jni.h",
+        "base/base_jni_headers/JavaHeapDumpGenerator_jni.h",
+        "base/base_jni_headers/LibraryLoader_jni.h",
+        "base/base_jni_headers/LibraryPrefetcher_jni.h",
+        "base/base_jni_headers/LocaleUtils_jni.h",
+        "base/base_jni_headers/MemoryPressureListener_jni.h",
+        "base/base_jni_headers/NativeUmaRecorder_jni.h",
+        "base/base_jni_headers/PathService_jni.h",
+        "base/base_jni_headers/PathUtils_jni.h",
+        "base/base_jni_headers/PostTask_jni.h",
+        "base/base_jni_headers/PowerMonitor_jni.h",
+        "base/base_jni_headers/RadioUtils_jni.h",
+        "base/base_jni_headers/StatisticsRecorderAndroid_jni.h",
+        "base/base_jni_headers/SysUtils_jni.h",
+        "base/base_jni_headers/TaskRunnerImpl_jni.h",
+        "base/base_jni_headers/ThreadUtils_jni.h",
+        "base/base_jni_headers/TimezoneUtils_jni.h",
+        "base/base_jni_headers/TraceEvent_jni.h",
+        "base/base_jni_headers/UnguessableToken_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:base_jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_base_base_jni_headers__android_x86",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/SysUtils.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/base_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "ApkAssets_jni.h " +
+         "--output_name " +
+         "ApplicationStatus_jni.h " +
+         "--output_name " +
+         "BaseFeatureList_jni.h " +
+         "--output_name " +
+         "BuildInfo_jni.h " +
+         "--output_name " +
+         "BundleUtils_jni.h " +
+         "--output_name " +
+         "Callback_jni.h " +
+         "--output_name " +
+         "CommandLine_jni.h " +
+         "--output_name " +
+         "ContentUriUtils_jni.h " +
+         "--output_name " +
+         "CpuFeatures_jni.h " +
+         "--output_name " +
+         "EarlyTraceEvent_jni.h " +
+         "--output_name " +
+         "EventLog_jni.h " +
+         "--output_name " +
+         "FeatureList_jni.h " +
+         "--output_name " +
+         "Features_jni.h " +
+         "--output_name " +
+         "FieldTrialList_jni.h " +
+         "--output_name " +
+         "FileUtils_jni.h " +
+         "--output_name " +
+         "ImportantFileWriterAndroid_jni.h " +
+         "--output_name " +
+         "IntStringCallback_jni.h " +
+         "--output_name " +
+         "JNIUtils_jni.h " +
+         "--output_name " +
+         "JavaExceptionReporter_jni.h " +
+         "--output_name " +
+         "JavaHandlerThread_jni.h " +
+         "--output_name " +
+         "LocaleUtils_jni.h " +
+         "--output_name " +
+         "MemoryPressureListener_jni.h " +
+         "--output_name " +
+         "PathService_jni.h " +
+         "--output_name " +
+         "PathUtils_jni.h " +
+         "--output_name " +
+         "PowerMonitor_jni.h " +
+         "--output_name " +
+         "RadioUtils_jni.h " +
+         "--output_name " +
+         "SysUtils_jni.h " +
+         "--output_name " +
+         "ThreadUtils_jni.h " +
+         "--output_name " +
+         "TimezoneUtils_jni.h " +
+         "--output_name " +
+         "TraceEvent_jni.h " +
+         "--output_name " +
+         "UnguessableToken_jni.h " +
+         "--output_name " +
+         "JankMetricUMARecorder_jni.h " +
+         "--output_name " +
+         "LibraryLoader_jni.h " +
+         "--output_name " +
+         "LibraryPrefetcher_jni.h " +
+         "--output_name " +
+         "JavaHeapDumpGenerator_jni.h " +
+         "--output_name " +
+         "NativeUmaRecorder_jni.h " +
+         "--output_name " +
+         "StatisticsRecorderAndroid_jni.h " +
+         "--output_name " +
+         "ChildProcessService_jni.h " +
+         "--output_name " +
+         "PostTask_jni.h " +
+         "--output_name " +
+         "TaskRunnerImpl_jni.h " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApkAssets.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApplicationStatus.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BaseFeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BuildInfo.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BundleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Callback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CommandLine.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ContentUriUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CpuFeatures.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EarlyTraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EventLog.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Features.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FieldTrialList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FileUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/IntStringCallback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JNIUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaExceptionReporter.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaHandlerThread.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/LocaleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/MemoryPressureListener.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PowerMonitor.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/RadioUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/SysUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ThreadUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TimezoneUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/UnguessableToken.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/PostTask.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java)",
+    out: [
+        "base/base_jni_headers/ApkAssets_jni.h",
+        "base/base_jni_headers/ApplicationStatus_jni.h",
+        "base/base_jni_headers/BaseFeatureList_jni.h",
+        "base/base_jni_headers/BuildInfo_jni.h",
+        "base/base_jni_headers/BundleUtils_jni.h",
+        "base/base_jni_headers/Callback_jni.h",
+        "base/base_jni_headers/ChildProcessService_jni.h",
+        "base/base_jni_headers/CommandLine_jni.h",
+        "base/base_jni_headers/ContentUriUtils_jni.h",
+        "base/base_jni_headers/CpuFeatures_jni.h",
+        "base/base_jni_headers/EarlyTraceEvent_jni.h",
+        "base/base_jni_headers/EventLog_jni.h",
+        "base/base_jni_headers/FeatureList_jni.h",
+        "base/base_jni_headers/Features_jni.h",
+        "base/base_jni_headers/FieldTrialList_jni.h",
+        "base/base_jni_headers/FileUtils_jni.h",
+        "base/base_jni_headers/ImportantFileWriterAndroid_jni.h",
+        "base/base_jni_headers/IntStringCallback_jni.h",
+        "base/base_jni_headers/JNIUtils_jni.h",
+        "base/base_jni_headers/JankMetricUMARecorder_jni.h",
+        "base/base_jni_headers/JavaExceptionReporter_jni.h",
+        "base/base_jni_headers/JavaHandlerThread_jni.h",
+        "base/base_jni_headers/JavaHeapDumpGenerator_jni.h",
+        "base/base_jni_headers/LibraryLoader_jni.h",
+        "base/base_jni_headers/LibraryPrefetcher_jni.h",
+        "base/base_jni_headers/LocaleUtils_jni.h",
+        "base/base_jni_headers/MemoryPressureListener_jni.h",
+        "base/base_jni_headers/NativeUmaRecorder_jni.h",
+        "base/base_jni_headers/PathService_jni.h",
+        "base/base_jni_headers/PathUtils_jni.h",
+        "base/base_jni_headers/PostTask_jni.h",
+        "base/base_jni_headers/PowerMonitor_jni.h",
+        "base/base_jni_headers/RadioUtils_jni.h",
+        "base/base_jni_headers/StatisticsRecorderAndroid_jni.h",
+        "base/base_jni_headers/SysUtils_jni.h",
+        "base/base_jni_headers/TaskRunnerImpl_jni.h",
+        "base/base_jni_headers/ThreadUtils_jni.h",
+        "base/base_jni_headers/TimezoneUtils_jni.h",
+        "base/base_jni_headers/TraceEvent_jni.h",
+        "base/base_jni_headers/UnguessableToken_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:base_jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_base_jni_headers__android_x86_64",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/SysUtils.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/base/base_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "ApkAssets_jni.h " +
+         "--output_name " +
+         "ApplicationStatus_jni.h " +
+         "--output_name " +
+         "BaseFeatureList_jni.h " +
+         "--output_name " +
+         "BuildInfo_jni.h " +
+         "--output_name " +
+         "BundleUtils_jni.h " +
+         "--output_name " +
+         "Callback_jni.h " +
+         "--output_name " +
+         "CommandLine_jni.h " +
+         "--output_name " +
+         "ContentUriUtils_jni.h " +
+         "--output_name " +
+         "CpuFeatures_jni.h " +
+         "--output_name " +
+         "EarlyTraceEvent_jni.h " +
+         "--output_name " +
+         "EventLog_jni.h " +
+         "--output_name " +
+         "FeatureList_jni.h " +
+         "--output_name " +
+         "Features_jni.h " +
+         "--output_name " +
+         "FieldTrialList_jni.h " +
+         "--output_name " +
+         "FileUtils_jni.h " +
+         "--output_name " +
+         "ImportantFileWriterAndroid_jni.h " +
+         "--output_name " +
+         "IntStringCallback_jni.h " +
+         "--output_name " +
+         "JNIUtils_jni.h " +
+         "--output_name " +
+         "JavaExceptionReporter_jni.h " +
+         "--output_name " +
+         "JavaHandlerThread_jni.h " +
+         "--output_name " +
+         "LocaleUtils_jni.h " +
+         "--output_name " +
+         "MemoryPressureListener_jni.h " +
+         "--output_name " +
+         "PathService_jni.h " +
+         "--output_name " +
+         "PathUtils_jni.h " +
+         "--output_name " +
+         "PowerMonitor_jni.h " +
+         "--output_name " +
+         "RadioUtils_jni.h " +
+         "--output_name " +
+         "SysUtils_jni.h " +
+         "--output_name " +
+         "ThreadUtils_jni.h " +
+         "--output_name " +
+         "TimezoneUtils_jni.h " +
+         "--output_name " +
+         "TraceEvent_jni.h " +
+         "--output_name " +
+         "UnguessableToken_jni.h " +
+         "--output_name " +
+         "JankMetricUMARecorder_jni.h " +
+         "--output_name " +
+         "LibraryLoader_jni.h " +
+         "--output_name " +
+         "LibraryPrefetcher_jni.h " +
+         "--output_name " +
+         "JavaHeapDumpGenerator_jni.h " +
+         "--output_name " +
+         "NativeUmaRecorder_jni.h " +
+         "--output_name " +
+         "StatisticsRecorderAndroid_jni.h " +
+         "--output_name " +
+         "ChildProcessService_jni.h " +
+         "--output_name " +
+         "PostTask_jni.h " +
+         "--output_name " +
+         "TaskRunnerImpl_jni.h " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApkAssets.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ApplicationStatus.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BaseFeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BuildInfo.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/BundleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Callback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CommandLine.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ContentUriUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/CpuFeatures.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EarlyTraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/EventLog.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FeatureList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/Features.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FieldTrialList.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/FileUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/IntStringCallback.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JNIUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaExceptionReporter.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/JavaHandlerThread.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/LocaleUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/MemoryPressureListener.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PathUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/PowerMonitor.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/RadioUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/SysUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/ThreadUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TimezoneUtils.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/TraceEvent.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/UnguessableToken.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/PostTask.java) " +
+         "--input_file " +
+         "$(location base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java)",
+    out: [
+        "base/base_jni_headers/ApkAssets_jni.h",
+        "base/base_jni_headers/ApplicationStatus_jni.h",
+        "base/base_jni_headers/BaseFeatureList_jni.h",
+        "base/base_jni_headers/BuildInfo_jni.h",
+        "base/base_jni_headers/BundleUtils_jni.h",
+        "base/base_jni_headers/Callback_jni.h",
+        "base/base_jni_headers/ChildProcessService_jni.h",
+        "base/base_jni_headers/CommandLine_jni.h",
+        "base/base_jni_headers/ContentUriUtils_jni.h",
+        "base/base_jni_headers/CpuFeatures_jni.h",
+        "base/base_jni_headers/EarlyTraceEvent_jni.h",
+        "base/base_jni_headers/EventLog_jni.h",
+        "base/base_jni_headers/FeatureList_jni.h",
+        "base/base_jni_headers/Features_jni.h",
+        "base/base_jni_headers/FieldTrialList_jni.h",
+        "base/base_jni_headers/FileUtils_jni.h",
+        "base/base_jni_headers/ImportantFileWriterAndroid_jni.h",
+        "base/base_jni_headers/IntStringCallback_jni.h",
+        "base/base_jni_headers/JNIUtils_jni.h",
+        "base/base_jni_headers/JankMetricUMARecorder_jni.h",
+        "base/base_jni_headers/JavaExceptionReporter_jni.h",
+        "base/base_jni_headers/JavaHandlerThread_jni.h",
+        "base/base_jni_headers/JavaHeapDumpGenerator_jni.h",
+        "base/base_jni_headers/LibraryLoader_jni.h",
+        "base/base_jni_headers/LibraryPrefetcher_jni.h",
+        "base/base_jni_headers/LocaleUtils_jni.h",
+        "base/base_jni_headers/MemoryPressureListener_jni.h",
+        "base/base_jni_headers/NativeUmaRecorder_jni.h",
+        "base/base_jni_headers/PathService_jni.h",
+        "base/base_jni_headers/PathUtils_jni.h",
+        "base/base_jni_headers/PostTask_jni.h",
+        "base/base_jni_headers/PowerMonitor_jni.h",
+        "base/base_jni_headers/RadioUtils_jni.h",
+        "base/base_jni_headers/StatisticsRecorderAndroid_jni.h",
+        "base/base_jni_headers/SysUtils_jni.h",
+        "base/base_jni_headers/TaskRunnerImpl_jni.h",
+        "base/base_jni_headers/ThreadUtils_jni.h",
+        "base/base_jni_headers/TimezoneUtils_jni.h",
+        "base/base_jni_headers/TraceEvent_jni.h",
+        "base/base_jni_headers/UnguessableToken_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:base_static
+cc_library_static {
+    name: "cronet_aml_base_base_static",
+    srcs: [
+        "base/base_switches.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //base:build_date__android_arm
+cc_genrule {
+    name: "cronet_aml_base_build_date__android_arm",
+    cmd: "$(location build/write_build_date_header.py) $(out) " +
+         "1664686800",
+    out: [
+        "base/generated_build_date.h",
+    ],
+    tool_files: [
+        "build/write_build_date_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:build_date__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_build_date__android_arm64",
+    cmd: "$(location build/write_build_date_header.py) $(out) " +
+         "1664686800",
+    out: [
+        "base/generated_build_date.h",
+    ],
+    tool_files: [
+        "build/write_build_date_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:build_date__android_x86
+cc_genrule {
+    name: "cronet_aml_base_build_date__android_x86",
+    cmd: "$(location build/write_build_date_header.py) $(out) " +
+         "1664686800",
+    out: [
+        "base/generated_build_date.h",
+    ],
+    tool_files: [
+        "build/write_build_date_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:build_date__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_build_date__android_x86_64",
+    cmd: "$(location build/write_build_date_header.py) $(out) " +
+         "1664686800",
+    out: [
+        "base/generated_build_date.h",
+    ],
+    tool_files: [
+        "build/write_build_date_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:build_date__host
+cc_genrule {
+    name: "cronet_aml_base_build_date__host",
+    cmd: "$(location build/write_build_date_header.py) $(out) " +
+         "1664686800",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/generated_build_date.h",
+    ],
+    tool_files: [
+        "build/write_build_date_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:cfi_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_cfi_buildflags__android_arm",
+    cmd: "echo '--flags CFI_CAST_CHECK=\"false && false\" CFI_DIAG=\"false && false\" CFI_ICALL_CHECK=\"false && false\" CFI_ENFORCEMENT_TRAP=\"false && !false\" CFI_ENFORCEMENT_DIAGNOSTIC=\"false && false && !false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:cfi_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/cfi_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:cfi_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_cfi_buildflags__android_arm64",
+    cmd: "echo '--flags CFI_CAST_CHECK=\"false && false\" CFI_DIAG=\"false && false\" CFI_ICALL_CHECK=\"false && false\" CFI_ENFORCEMENT_TRAP=\"false && !false\" CFI_ENFORCEMENT_DIAGNOSTIC=\"false && false && !false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:cfi_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/cfi_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:cfi_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_cfi_buildflags__android_x86",
+    cmd: "echo '--flags CFI_CAST_CHECK=\"false && false\" CFI_DIAG=\"false && false\" CFI_ICALL_CHECK=\"false && false\" CFI_ENFORCEMENT_TRAP=\"false && !false\" CFI_ENFORCEMENT_DIAGNOSTIC=\"false && false && !false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:cfi_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/cfi_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:cfi_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_cfi_buildflags__android_x86_64",
+    cmd: "echo '--flags CFI_CAST_CHECK=\"false && false\" CFI_DIAG=\"false && false\" CFI_ICALL_CHECK=\"false && false\" CFI_ENFORCEMENT_TRAP=\"false && !false\" CFI_ENFORCEMENT_DIAGNOSTIC=\"false && false && !false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:cfi_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/cfi_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:cfi_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_cfi_buildflags__host",
+    cmd: "echo '--flags CFI_CAST_CHECK=\"false && false\" CFI_DIAG=\"false && false\" CFI_ICALL_CHECK=\"false && false\" CFI_ENFORCEMENT_TRAP=\"false && !false\" CFI_ENFORCEMENT_DIAGNOSTIC=\"false && false && !false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:cfi_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/cfi_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:clang_profiling_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_clang_profiling_buildflags__android_arm",
+    cmd: "echo '--flags CLANG_PROFILING=\"false\" CLANG_PROFILING_INSIDE_SANDBOX=\"false\" USE_CLANG_COVERAGE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:clang_profiling_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/clang_profiling_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:clang_profiling_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_clang_profiling_buildflags__android_arm64",
+    cmd: "echo '--flags CLANG_PROFILING=\"false\" CLANG_PROFILING_INSIDE_SANDBOX=\"false\" USE_CLANG_COVERAGE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:clang_profiling_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/clang_profiling_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:clang_profiling_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_clang_profiling_buildflags__android_x86",
+    cmd: "echo '--flags CLANG_PROFILING=\"false\" CLANG_PROFILING_INSIDE_SANDBOX=\"false\" USE_CLANG_COVERAGE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:clang_profiling_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/clang_profiling_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:clang_profiling_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_clang_profiling_buildflags__android_x86_64",
+    cmd: "echo '--flags CLANG_PROFILING=\"false\" CLANG_PROFILING_INSIDE_SANDBOX=\"false\" USE_CLANG_COVERAGE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:clang_profiling_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/clang_profiling_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:clang_profiling_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_clang_profiling_buildflags__host",
+    cmd: "echo '--flags CLANG_PROFILING=\"false\" CLANG_PROFILING_INSIDE_SANDBOX=\"false\" USE_CLANG_COVERAGE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:clang_profiling_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/clang_profiling_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:debugging_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_debugging_buildflags__android_arm",
+    cmd: "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"false\" UNSAFE_DEVELOPER_BUILD=\"true\" CAN_UNWIND_WITH_CFI_TABLE=\"true\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"true\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"true\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:debugging_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_debugging_buildflags__android_arm64",
+    cmd: "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"true\" UNSAFE_DEVELOPER_BUILD=\"true\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"true\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"true\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:debugging_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_debugging_buildflags__android_x86",
+    cmd: "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"true\" UNSAFE_DEVELOPER_BUILD=\"true\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"true\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"true\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:debugging_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_debugging_buildflags__android_x86_64",
+    cmd: "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"false\" UNSAFE_DEVELOPER_BUILD=\"true\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"true\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"true\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:debugging_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_debugging_buildflags__host",
+    cmd: "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"true\" UNSAFE_DEVELOPER_BUILD=\"true\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"true\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"true\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:debugging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/debug/debugging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:feature_list_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_feature_list_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_BANNED_BASE_FEATURE_PREFIX=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:feature_list_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/feature_list_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:feature_list_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_feature_list_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_BANNED_BASE_FEATURE_PREFIX=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:feature_list_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/feature_list_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:feature_list_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_feature_list_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_BANNED_BASE_FEATURE_PREFIX=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:feature_list_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/feature_list_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:feature_list_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_feature_list_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_BANNED_BASE_FEATURE_PREFIX=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:feature_list_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/feature_list_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:feature_list_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_feature_list_buildflags__host",
+    cmd: "echo '--flags ENABLE_BANNED_BASE_FEATURE_PREFIX=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:feature_list_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/feature_list_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:ios_cronet_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_ios_cronet_buildflags__android_arm",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:ios_cronet_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_ios_cronet_buildflags__android_arm64",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:ios_cronet_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_ios_cronet_buildflags__android_x86",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:ios_cronet_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_ios_cronet_buildflags__android_x86_64",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:ios_cronet_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_ios_cronet_buildflags__host",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/message_loop/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:java_features_srcjar
+java_genrule {
+    name: "cronet_aml_base_java_features_srcjar",
+    srcs: [
+        "base/android/base_features.cc",
+        "base/features.cc",
+        "base/task/task_features.cc",
+    ],
+    cmd: "$(location build/android/gyp/java_cpp_features.py) --srcjar " +
+         "$(out) " +
+         "--template " +
+         "$(location base/android/java/src/org/chromium/base/BaseFeatures.java.tmpl) " +
+         "$(location base/android/base_features.cc) " +
+         "$(location base/features.cc) " +
+         "$(location base/task/task_features.cc)",
+    out: [
+        "base/java_features_srcjar.srcjar",
+    ],
+    tool_files: [
+        "base/android/java/src/org/chromium/base/BaseFeatures.java.tmpl",
+        "build/android/gyp/java_cpp_features.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+    ],
+}
+
+// GN: //base:java_switches_srcjar
+java_genrule {
+    name: "cronet_aml_base_java_switches_srcjar",
+    srcs: [
+        "base/base_switches.cc",
+    ],
+    cmd: "$(location build/android/gyp/java_cpp_strings.py) --srcjar " +
+         "$(out) " +
+         "--template " +
+         "$(location base/android/java/src/org/chromium/base/BaseSwitches.java.tmpl) " +
+         "$(location base/base_switches.cc)",
+    out: [
+        "base/java_switches_srcjar.srcjar",
+    ],
+    tool_files: [
+        "base/android/java/src/org/chromium/base/BaseSwitches.java.tmpl",
+        "build/android/gyp/java_cpp_strings.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+    ],
+}
+
+// GN: //base:logging_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_logging_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_LOG_ERROR_NOT_REACHED=\"false\" USE_RUNTIME_VLOG=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:logging_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_logging_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_LOG_ERROR_NOT_REACHED=\"false\" USE_RUNTIME_VLOG=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:logging_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_logging_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_LOG_ERROR_NOT_REACHED=\"false\" USE_RUNTIME_VLOG=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:logging_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_logging_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_LOG_ERROR_NOT_REACHED=\"false\" USE_RUNTIME_VLOG=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:logging_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_logging_buildflags__host",
+    cmd: "echo '--flags ENABLE_LOG_ERROR_NOT_REACHED=\"false\" USE_RUNTIME_VLOG=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:logging_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/logging_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:message_pump_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_message_pump_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_MESSAGE_PUMP_EPOLL=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:message_pump_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/message_pump_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:message_pump_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_message_pump_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_MESSAGE_PUMP_EPOLL=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:message_pump_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/message_pump_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:message_pump_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_message_pump_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_MESSAGE_PUMP_EPOLL=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:message_pump_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/message_pump_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:message_pump_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_message_pump_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_MESSAGE_PUMP_EPOLL=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:message_pump_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/message_loop/message_pump_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:message_pump_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_message_pump_buildflags__host",
+    cmd: "echo '--flags ENABLE_MESSAGE_PUMP_EPOLL=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:message_pump_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/message_loop/message_pump_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/numerics:base_numerics
+cc_object {
+    name: "cronet_aml_base_numerics_base_numerics",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //base:orderfile_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_orderfile_buildflags__android_arm",
+    cmd: "echo '--flags DEVTOOLS_INSTRUMENTATION_DUMPING=\"false\" ORDERFILE_INSTRUMENTATION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:orderfile_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/orderfile/orderfile_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:orderfile_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_orderfile_buildflags__android_arm64",
+    cmd: "echo '--flags DEVTOOLS_INSTRUMENTATION_DUMPING=\"false\" ORDERFILE_INSTRUMENTATION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:orderfile_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/orderfile/orderfile_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:orderfile_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_orderfile_buildflags__android_x86",
+    cmd: "echo '--flags DEVTOOLS_INSTRUMENTATION_DUMPING=\"false\" ORDERFILE_INSTRUMENTATION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:orderfile_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/orderfile/orderfile_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:orderfile_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_orderfile_buildflags__android_x86_64",
+    cmd: "echo '--flags DEVTOOLS_INSTRUMENTATION_DUMPING=\"false\" ORDERFILE_INSTRUMENTATION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:orderfile_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/android/orderfile/orderfile_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:orderfile_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_orderfile_buildflags__host",
+    cmd: "echo '--flags DEVTOOLS_INSTRUMENTATION_DUMPING=\"false\" ORDERFILE_INSTRUMENTATION=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:orderfile_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/android/orderfile/orderfile_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:parsing_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_parsing_buildflags__android_arm",
+    cmd: "echo '--flags BUILD_RUST_JSON_PARSER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:parsing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/parsing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:parsing_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_parsing_buildflags__android_arm64",
+    cmd: "echo '--flags BUILD_RUST_JSON_PARSER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:parsing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/parsing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:parsing_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_parsing_buildflags__android_x86",
+    cmd: "echo '--flags BUILD_RUST_JSON_PARSER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:parsing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/parsing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:parsing_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_parsing_buildflags__android_x86_64",
+    cmd: "echo '--flags BUILD_RUST_JSON_PARSER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:parsing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/parsing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:parsing_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_parsing_buildflags__host",
+    cmd: "echo '--flags BUILD_RUST_JSON_PARSER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:parsing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/parsing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:power_monitor_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_power_monitor_buildflags__android_arm",
+    cmd: "echo '--flags HAS_BATTERY_LEVEL_PROVIDER_IMPL=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:power_monitor_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/power_monitor/power_monitor_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:power_monitor_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_power_monitor_buildflags__android_arm64",
+    cmd: "echo '--flags HAS_BATTERY_LEVEL_PROVIDER_IMPL=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:power_monitor_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/power_monitor/power_monitor_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:power_monitor_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_power_monitor_buildflags__android_x86",
+    cmd: "echo '--flags HAS_BATTERY_LEVEL_PROVIDER_IMPL=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:power_monitor_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/power_monitor/power_monitor_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:power_monitor_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_power_monitor_buildflags__android_x86_64",
+    cmd: "echo '--flags HAS_BATTERY_LEVEL_PROVIDER_IMPL=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:power_monitor_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/power_monitor/power_monitor_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:power_monitor_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_power_monitor_buildflags__host",
+    cmd: "echo '--flags HAS_BATTERY_LEVEL_PROVIDER_IMPL=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:power_monitor_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/power_monitor/power_monitor_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:profiler_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_profiler_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_ARM_CFI_TABLE=\"true\" IOS_STACK_PROFILER_ENABLED=\"true\" USE_ANDROID_UNWINDER_V2=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:profiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/profiler/profiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:profiler_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_profiler_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_ARM_CFI_TABLE=\"false\" IOS_STACK_PROFILER_ENABLED=\"true\" USE_ANDROID_UNWINDER_V2=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:profiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/profiler/profiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:profiler_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_profiler_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_ARM_CFI_TABLE=\"false\" IOS_STACK_PROFILER_ENABLED=\"true\" USE_ANDROID_UNWINDER_V2=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:profiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/profiler/profiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:profiler_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_profiler_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_ARM_CFI_TABLE=\"false\" IOS_STACK_PROFILER_ENABLED=\"true\" USE_ANDROID_UNWINDER_V2=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:profiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/profiler/profiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:profiler_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_profiler_buildflags__host",
+    cmd: "echo '--flags ENABLE_ARM_CFI_TABLE=\"false\" IOS_STACK_PROFILER_ENABLED=\"true\" USE_ANDROID_UNWINDER_V2=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:profiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/profiler/profiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:sanitizer_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_sanitizer_buildflags__android_arm",
+    cmd: "echo '--flags IS_HWASAN=\"false\" USING_SANITIZER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:sanitizer_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/sanitizer_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:sanitizer_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_sanitizer_buildflags__android_arm64",
+    cmd: "echo '--flags IS_HWASAN=\"false\" USING_SANITIZER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:sanitizer_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/sanitizer_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:sanitizer_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_sanitizer_buildflags__android_x86",
+    cmd: "echo '--flags IS_HWASAN=\"false\" USING_SANITIZER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:sanitizer_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/sanitizer_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:sanitizer_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_sanitizer_buildflags__android_x86_64",
+    cmd: "echo '--flags IS_HWASAN=\"false\" USING_SANITIZER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:sanitizer_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/sanitizer_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:sanitizer_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_sanitizer_buildflags__host",
+    cmd: "echo '--flags IS_HWASAN=\"false\" USING_SANITIZER=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:sanitizer_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/sanitizer_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:synchronization_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_synchronization_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_MUTEX_PRIORITY_INHERITANCE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:synchronization_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/synchronization/synchronization_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:synchronization_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_synchronization_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_MUTEX_PRIORITY_INHERITANCE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:synchronization_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/synchronization/synchronization_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:synchronization_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_synchronization_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_MUTEX_PRIORITY_INHERITANCE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:synchronization_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/synchronization/synchronization_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:synchronization_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_synchronization_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_MUTEX_PRIORITY_INHERITANCE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:synchronization_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/synchronization/synchronization_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:synchronization_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_synchronization_buildflags__host",
+    cmd: "echo '--flags ENABLE_MUTEX_PRIORITY_INHERITANCE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:synchronization_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/synchronization/synchronization_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base/third_party/double_conversion:double_conversion
+cc_library_static {
+    name: "cronet_aml_base_third_party_double_conversion_double_conversion",
+    srcs: [
+        "base/third_party/double_conversion/double-conversion/bignum-dtoa.cc",
+        "base/third_party/double_conversion/double-conversion/bignum.cc",
+        "base/third_party/double_conversion/double-conversion/cached-powers.cc",
+        "base/third_party/double_conversion/double-conversion/double-to-string.cc",
+        "base/third_party/double_conversion/double-conversion/fast-dtoa.cc",
+        "base/third_party/double_conversion/double-conversion/fixed-dtoa.cc",
+        "base/third_party/double_conversion/double-conversion/string-to-double.cc",
+        "base/third_party/double_conversion/double-conversion/strtod.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //base/third_party/dynamic_annotations:dynamic_annotations
+cc_library_static {
+    name: "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+    srcs: [
+        "base/third_party/dynamic_annotations/dynamic_annotations.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //base/third_party/symbolize:symbolize
+cc_library_static {
+    name: "cronet_aml_base_third_party_symbolize_symbolize",
+    srcs: [
+        "base/third_party/symbolize/demangle.cc",
+        "base/third_party/symbolize/symbolize.cc",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGLOG_EXPORT=",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //base/third_party/xdg_mime:xdg_mime
+cc_library_static {
+    name: "cronet_aml_base_third_party_xdg_mime_xdg_mime",
+    srcs: [
+        "base/third_party/xdg_mime/xdgmime.c",
+        "base/third_party/xdg_mime/xdgmimealias.c",
+        "base/third_party/xdg_mime/xdgmimecache.c",
+        "base/third_party/xdg_mime/xdgmimeglob.c",
+        "base/third_party/xdg_mime/xdgmimeicon.c",
+        "base/third_party/xdg_mime/xdgmimeint.c",
+        "base/third_party/xdg_mime/xdgmimemagic.c",
+        "base/third_party/xdg_mime/xdgmimeparent.c",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //base/third_party/xdg_user_dirs:xdg_user_dirs
+cc_library_static {
+    name: "cronet_aml_base_third_party_xdg_user_dirs_xdg_user_dirs",
+    srcs: [
+        "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //base:tracing_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_base_tracing_buildflags__android_arm",
+    cmd: "echo '--flags ENABLE_BASE_TRACING=\"false\" USE_PERFETTO_CLIENT_LIBRARY=\"false\" OPTIONAL_TRACE_EVENTS_ENABLED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:tracing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/tracing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:tracing_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_base_tracing_buildflags__android_arm64",
+    cmd: "echo '--flags ENABLE_BASE_TRACING=\"false\" USE_PERFETTO_CLIENT_LIBRARY=\"false\" OPTIONAL_TRACE_EVENTS_ENABLED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:tracing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/tracing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:tracing_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_base_tracing_buildflags__android_x86",
+    cmd: "echo '--flags ENABLE_BASE_TRACING=\"false\" USE_PERFETTO_CLIENT_LIBRARY=\"false\" OPTIONAL_TRACE_EVENTS_ENABLED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:tracing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/tracing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:tracing_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_base_tracing_buildflags__android_x86_64",
+    cmd: "echo '--flags ENABLE_BASE_TRACING=\"false\" USE_PERFETTO_CLIENT_LIBRARY=\"false\" OPTIONAL_TRACE_EVENTS_ENABLED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:tracing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "base/tracing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //base:tracing_buildflags__host
+cc_genrule {
+    name: "cronet_aml_base_tracing_buildflags__host",
+    cmd: "echo '--flags ENABLE_BASE_TRACING=\"false\" USE_PERFETTO_CLIENT_LIBRARY=\"false\" OPTIONAL_TRACE_EVENTS_ENABLED=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//base:tracing_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "base/tracing_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/android:build_config_gen
+genrule {
+    name: "cronet_aml_build_android_build_config_gen",
+    srcs: [
+        ":cronet_aml_build_android_build_config_gen_preprocess",
+    ],
+    tools: [
+        "soong_zip",
+    ],
+    cmd: "cp $(in) $(genDir)/BuildConfig.java && " +
+         "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/BuildConfig.java",
+    out: [
+        "BuildConfig.srcjar",
+    ],
+}
+
+// GN: //build/android:build_config_gen
+cc_object {
+    name: "cronet_aml_build_android_build_config_gen_preprocess",
+    srcs: [
+        ":cronet_aml_build_android_build_config_gen_rename",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-D_ENABLE_ASSERTS",
+        "-E",
+        "-P",
+    ],
+    compile_multilib: "32",
+}
+
+// GN: //build/android:build_config_gen
+genrule {
+    name: "cronet_aml_build_android_build_config_gen_rename",
+    srcs: [
+        "build/android/java/templates/BuildConfig.template",
+    ],
+    cmd: "cp $(in) $(out)",
+    out: [
+        "BuildConfig.cc",
+    ],
+}
+
+// GN: //build/android:native_libraries_gen
+java_genrule {
+    name: "cronet_aml_build_android_native_libraries_gen",
+    cmd: "$(location build/android/gyp/write_native_libraries_java.py) --output " +
+         "$(out) " +
+         "--cpu-family " +
+         "CPU_FAMILY_ARM",
+    out: [
+        "build/android/native_libraries_gen.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/write_native_libraries_java.py",
+        "build/gn_helpers.py",
+    ],
+}
+
+// GN: //build:branding_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_build_branding_buildflags__android_arm",
+    cmd: "echo '--flags CHROMIUM_BRANDING=\"1\" GOOGLE_CHROME_BRANDING=\"0\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:branding_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/branding_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:branding_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_build_branding_buildflags__android_arm64",
+    cmd: "echo '--flags CHROMIUM_BRANDING=\"1\" GOOGLE_CHROME_BRANDING=\"0\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:branding_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/branding_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:branding_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_build_branding_buildflags__android_x86",
+    cmd: "echo '--flags CHROMIUM_BRANDING=\"1\" GOOGLE_CHROME_BRANDING=\"0\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:branding_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/branding_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:branding_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_build_branding_buildflags__android_x86_64",
+    cmd: "echo '--flags CHROMIUM_BRANDING=\"1\" GOOGLE_CHROME_BRANDING=\"0\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:branding_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/branding_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:branding_buildflags__host
+cc_genrule {
+    name: "cronet_aml_build_branding_buildflags__host",
+    cmd: "echo '--flags CHROMIUM_BRANDING=\"1\" GOOGLE_CHROME_BRANDING=\"0\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:branding_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "build/branding_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:buildflag_header_h
+cc_object {
+    name: "cronet_aml_build_buildflag_header_h",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //build:chromecast_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_build_chromecast_buildflags__android_arm",
+    cmd: "echo '--flags IS_CASTOS=\"false\" IS_CAST_ANDROID=\"false\" ENABLE_CAST_RECEIVER=\"false\" IS_CHROMECAST=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromecast_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_build_chromecast_buildflags__android_arm64",
+    cmd: "echo '--flags IS_CASTOS=\"false\" IS_CAST_ANDROID=\"false\" ENABLE_CAST_RECEIVER=\"false\" IS_CHROMECAST=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromecast_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_build_chromecast_buildflags__android_x86",
+    cmd: "echo '--flags IS_CASTOS=\"false\" IS_CAST_ANDROID=\"false\" ENABLE_CAST_RECEIVER=\"false\" IS_CHROMECAST=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromecast_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_build_chromecast_buildflags__android_x86_64",
+    cmd: "echo '--flags IS_CASTOS=\"false\" IS_CAST_ANDROID=\"false\" ENABLE_CAST_RECEIVER=\"false\" IS_CHROMECAST=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromecast_buildflags__host
+cc_genrule {
+    name: "cronet_aml_build_chromecast_buildflags__host",
+    cmd: "echo '--flags IS_CASTOS=\"false\" IS_CAST_ANDROID=\"false\" ENABLE_CAST_RECEIVER=\"false\" IS_CHROMECAST=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromecast_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "build/chromecast_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromeos_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_build_chromeos_buildflags__android_arm",
+    cmd: "echo '--flags IS_CHROMEOS_DEVICE=\"false\" IS_CHROMEOS_LACROS=\"false\" IS_CHROMEOS_ASH=\"false\" IS_CHROMEOS_WITH_HW_DETAILS=\"false\" IS_REVEN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromeos_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_build_chromeos_buildflags__android_arm64",
+    cmd: "echo '--flags IS_CHROMEOS_DEVICE=\"false\" IS_CHROMEOS_LACROS=\"false\" IS_CHROMEOS_ASH=\"false\" IS_CHROMEOS_WITH_HW_DETAILS=\"false\" IS_REVEN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromeos_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_build_chromeos_buildflags__android_x86",
+    cmd: "echo '--flags IS_CHROMEOS_DEVICE=\"false\" IS_CHROMEOS_LACROS=\"false\" IS_CHROMEOS_ASH=\"false\" IS_CHROMEOS_WITH_HW_DETAILS=\"false\" IS_REVEN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromeos_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_build_chromeos_buildflags__android_x86_64",
+    cmd: "echo '--flags IS_CHROMEOS_DEVICE=\"false\" IS_CHROMEOS_LACROS=\"false\" IS_CHROMEOS_ASH=\"false\" IS_CHROMEOS_WITH_HW_DETAILS=\"false\" IS_REVEN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build:chromeos_buildflags__host
+cc_genrule {
+    name: "cronet_aml_build_chromeos_buildflags__host",
+    cmd: "echo '--flags IS_CHROMEOS_DEVICE=\"false\" IS_CHROMEOS_LACROS=\"false\" IS_CHROMEOS_ASH=\"false\" IS_CHROMEOS_WITH_HW_DETAILS=\"false\" IS_REVEN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build:chromeos_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "build/chromeos_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/config/compiler:compiler_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_build_config_compiler_compiler_buildflags__android_arm",
+    cmd: "echo '--flags CLANG_PGO=\"0\" SYMBOL_LEVEL=\"1\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build/config/compiler:compiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/config/compiler/compiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/config/compiler:compiler_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_build_config_compiler_compiler_buildflags__android_arm64",
+    cmd: "echo '--flags CLANG_PGO=\"0\" SYMBOL_LEVEL=\"1\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build/config/compiler:compiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/config/compiler/compiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/config/compiler:compiler_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_build_config_compiler_compiler_buildflags__android_x86",
+    cmd: "echo '--flags CLANG_PGO=\"0\" SYMBOL_LEVEL=\"1\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build/config/compiler:compiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/config/compiler/compiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/config/compiler:compiler_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_build_config_compiler_compiler_buildflags__android_x86_64",
+    cmd: "echo '--flags CLANG_PGO=\"0\" SYMBOL_LEVEL=\"1\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build/config/compiler:compiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "build/config/compiler/compiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //build/config/compiler:compiler_buildflags__host
+cc_genrule {
+    name: "cronet_aml_build_config_compiler_compiler_buildflags__host",
+    cmd: "echo '--flags CLANG_PGO=\"0\" SYMBOL_LEVEL=\"2\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//build/config/compiler:compiler_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "build/config/compiler/compiler_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //buildtools/third_party/libc++:libc++
+cc_object {
+    name: "cronet_aml_buildtools_third_party_libc___libc__",
+    srcs: [
+        "buildtools/third_party/libc++/trunk/src/algorithm.cpp",
+        "buildtools/third_party/libc++/trunk/src/any.cpp",
+        "buildtools/third_party/libc++/trunk/src/atomic.cpp",
+        "buildtools/third_party/libc++/trunk/src/barrier.cpp",
+        "buildtools/third_party/libc++/trunk/src/bind.cpp",
+        "buildtools/third_party/libc++/trunk/src/charconv.cpp",
+        "buildtools/third_party/libc++/trunk/src/chrono.cpp",
+        "buildtools/third_party/libc++/trunk/src/condition_variable.cpp",
+        "buildtools/third_party/libc++/trunk/src/condition_variable_destructor.cpp",
+        "buildtools/third_party/libc++/trunk/src/exception.cpp",
+        "buildtools/third_party/libc++/trunk/src/format.cpp",
+        "buildtools/third_party/libc++/trunk/src/functional.cpp",
+        "buildtools/third_party/libc++/trunk/src/future.cpp",
+        "buildtools/third_party/libc++/trunk/src/hash.cpp",
+        "buildtools/third_party/libc++/trunk/src/ios.cpp",
+        "buildtools/third_party/libc++/trunk/src/ios.instantiations.cpp",
+        "buildtools/third_party/libc++/trunk/src/iostream.cpp",
+        "buildtools/third_party/libc++/trunk/src/legacy_pointer_safety.cpp",
+        "buildtools/third_party/libc++/trunk/src/locale.cpp",
+        "buildtools/third_party/libc++/trunk/src/memory.cpp",
+        "buildtools/third_party/libc++/trunk/src/mutex.cpp",
+        "buildtools/third_party/libc++/trunk/src/mutex_destructor.cpp",
+        "buildtools/third_party/libc++/trunk/src/new.cpp",
+        "buildtools/third_party/libc++/trunk/src/optional.cpp",
+        "buildtools/third_party/libc++/trunk/src/random.cpp",
+        "buildtools/third_party/libc++/trunk/src/random_shuffle.cpp",
+        "buildtools/third_party/libc++/trunk/src/regex.cpp",
+        "buildtools/third_party/libc++/trunk/src/ryu/d2fixed.cpp",
+        "buildtools/third_party/libc++/trunk/src/ryu/d2s.cpp",
+        "buildtools/third_party/libc++/trunk/src/ryu/f2s.cpp",
+        "buildtools/third_party/libc++/trunk/src/shared_mutex.cpp",
+        "buildtools/third_party/libc++/trunk/src/stdexcept.cpp",
+        "buildtools/third_party/libc++/trunk/src/string.cpp",
+        "buildtools/third_party/libc++/trunk/src/strstream.cpp",
+        "buildtools/third_party/libc++/trunk/src/system_error.cpp",
+        "buildtools/third_party/libc++/trunk/src/thread.cpp",
+        "buildtools/third_party/libc++/trunk/src/typeinfo.cpp",
+        "buildtools/third_party/libc++/trunk/src/utility.cpp",
+        "buildtools/third_party/libc++/trunk/src/valarray.cpp",
+        "buildtools/third_party/libc++/trunk/src/variant.cpp",
+        "buildtools/third_party/libc++/trunk/src/vector.cpp",
+        "buildtools/third_party/libc++/trunk/src/verbose_abort.cpp",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DLIBCXX_BUILDING_LIBCXXABI",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_BUILDING_LIBRARY",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__((__visibility__(\"default\")))",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++/trunk/src/",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    cppflags: [
+        "-fexceptions",
+    ],
+    rtti: true,
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //buildtools/third_party/libc++abi:libc++abi
+cc_object {
+    name: "cronet_aml_buildtools_third_party_libc__abi_libc__abi",
+    srcs: [
+        "buildtools/third_party/libc++abi/trunk/src/abort_message.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_aux_runtime.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_default_handlers.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_exception.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_exception_storage.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_guard.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_handlers.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_personality.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_thread_atexit.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_vector.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/cxa_virtual.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/fallback_malloc.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/private_typeinfo.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/stdlib_exception.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/stdlib_stdexcept.cpp",
+        "buildtools/third_party/libc++abi/trunk/src/stdlib_typeinfo.cpp",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DLIBCXXABI_SILENT_TERMINATE",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_BUILDING_LIBRARY",
+        "-D_LIBCPP_CONSTINIT=constinit",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++/trunk/src/",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    cppflags: [
+        "-fexceptions",
+    ],
+    rtti: true,
+    target: {
+        android_arm: {
+            srcs: [
+                "buildtools/third_party/libc++abi/cxa_demangle_stub.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                "buildtools/third_party/libc++abi/cxa_demangle_stub.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                "buildtools/third_party/libc++abi/cxa_demangle_stub.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                "buildtools/third_party/libc++abi/cxa_demangle_stub.cc",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            srcs: [
+                "buildtools/third_party/libc++abi/trunk/src/cxa_demangle.cpp",
+            ],
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //buildtools/third_party/libunwind:libunwind
+cc_object {
+    name: "cronet_aml_buildtools_third_party_libunwind_libunwind",
+    srcs: [
+        "buildtools/third_party/libunwind/trunk/src/Unwind-EHABI.cpp",
+        "buildtools/third_party/libunwind/trunk/src/Unwind-sjlj.c",
+        "buildtools/third_party/libunwind/trunk/src/UnwindLevel1-gcc-ext.c",
+        "buildtools/third_party/libunwind/trunk/src/UnwindLevel1.c",
+        "buildtools/third_party/libunwind/trunk/src/UnwindRegistersRestore.S",
+        "buildtools/third_party/libunwind/trunk/src/UnwindRegistersSave.S",
+        "buildtools/third_party/libunwind/trunk/src/libunwind.cpp",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBUNWIND_IS_NATIVE_ONLY",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "buildtools/third_party/libunwind/trunk/include/",
+    ],
+    cpp_std: "c++20",
+    cppflags: [
+        "-fexceptions",
+    ],
+    rtti: true,
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/android:buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_buildflags__android_arm",
+    cmd: "echo '--flags INTEGRATED_MODE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet/android:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/android/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_buildflags__android_arm64",
+    cmd: "echo '--flags INTEGRATED_MODE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet/android:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/android/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_buildflags__android_x86",
+    cmd: "echo '--flags INTEGRATED_MODE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet/android:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/android/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_buildflags__android_x86_64",
+    cmd: "echo '--flags INTEGRATED_MODE=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet/android:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/android/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet
+cc_library_shared {
+    name: "cronet_aml_components_cronet_android_cronet",
+    srcs: [
+        ":cronet_aml_buildtools_third_party_libc___libc__",
+        ":cronet_aml_buildtools_third_party_libc__abi_libc__abi",
+        ":cronet_aml_buildtools_third_party_libunwind_libunwind",
+        ":cronet_aml_components_cronet_android_cronet_static",
+        ":cronet_aml_components_cronet_cronet_common",
+        ":cronet_aml_components_cronet_metrics_util",
+        ":cronet_aml_components_cronet_native_cronet_native_impl",
+        ":cronet_aml_components_grpc_support_grpc_support",
+        ":cronet_aml_components_metrics_library_support",
+        "components/cronet/android/cronet_jni.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_components_prefs_prefs",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_net",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_components_cronet_android_cronet_jni_registration",
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    export_generated_headers: [
+        "cronet_aml_components_cronet_android_cronet_jni_registration",
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    linker_scripts: [
+        "base/android/library_loader/anchor_functions.lds",
+    ],
+    stem: "libcronet.109.0.5386.0",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86_64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86_64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/android:cronet_jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/cronet/android/cronet_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "CronetBidirectionalStream_jni.h " +
+         "--output_name " +
+         "CronetLibraryLoader_jni.h " +
+         "--output_name " +
+         "CronetUploadDataStream_jni.h " +
+         "--output_name " +
+         "CronetUrlRequest_jni.h " +
+         "--output_name " +
+         "CronetUrlRequestContext_jni.h " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java)",
+    out: [
+        "components/cronet/android/cronet_jni_headers/CronetBidirectionalStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUploadDataStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm64",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/cronet/android/cronet_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "CronetBidirectionalStream_jni.h " +
+         "--output_name " +
+         "CronetLibraryLoader_jni.h " +
+         "--output_name " +
+         "CronetUploadDataStream_jni.h " +
+         "--output_name " +
+         "CronetUrlRequest_jni.h " +
+         "--output_name " +
+         "CronetUrlRequestContext_jni.h " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java)",
+    out: [
+        "components/cronet/android/cronet_jni_headers/CronetBidirectionalStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUploadDataStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/cronet/android/cronet_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "CronetBidirectionalStream_jni.h " +
+         "--output_name " +
+         "CronetLibraryLoader_jni.h " +
+         "--output_name " +
+         "CronetUploadDataStream_jni.h " +
+         "--output_name " +
+         "CronetUrlRequest_jni.h " +
+         "--output_name " +
+         "CronetUrlRequestContext_jni.h " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java)",
+    out: [
+        "components/cronet/android/cronet_jni_headers/CronetBidirectionalStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUploadDataStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86_64",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/cronet/android/cronet_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "CronetBidirectionalStream_jni.h " +
+         "--output_name " +
+         "CronetLibraryLoader_jni.h " +
+         "--output_name " +
+         "CronetUploadDataStream_jni.h " +
+         "--output_name " +
+         "CronetUrlRequest_jni.h " +
+         "--output_name " +
+         "CronetUrlRequestContext_jni.h " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java) " +
+         "--input_file " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java)",
+    out: [
+        "components/cronet/android/cronet_jni_headers/CronetBidirectionalStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUploadDataStream_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h",
+        "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_jni_registration
+cc_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_registration",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ActivityState.java",
+        "base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java",
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/ByteArrayGenerator.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CallbackController.java",
+        "base/android/java/src/org/chromium/base/CollectionUtil.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/CommandLineInitUtil.java",
+        "base/android/java/src/org/chromium/base/Consumer.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/ContextUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/DiscardableReferencePool.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/Function.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/IntentUtils.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/JniException.java",
+        "base/android/java/src/org/chromium/base/JniStaticTestMocker.java",
+        "base/android/java/src/org/chromium/base/LifetimeAssert.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/Log.java",
+        "base/android/java/src/org/chromium/base/MathUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
+        "base/android/java/src/org/chromium/base/ObserverList.java",
+        "base/android/java/src/org/chromium/base/PackageManagerUtils.java",
+        "base/android/java/src/org/chromium/base/PackageUtils.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PiiElider.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/PowerMonitorForQ.java",
+        "base/android/java/src/org/chromium/base/Predicate.java",
+        "base/android/java/src/org/chromium/base/Promise.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/StreamUtil.java",
+        "base/android/java/src/org/chromium/base/StrictModeContext.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimeUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/UnownedUserData.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataHost.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataKey.java",
+        "base/android/java/src/org/chromium/base/UserData.java",
+        "base/android/java/src/org/chromium/base/UserDataHost.java",
+        "base/android/java/src/org/chromium/base/WrappedClassLoader.java",
+        "base/android/java/src/org/chromium/base/annotations/AccessedByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeForTesting.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
+        "base/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
+        "base/android/java/src/org/chromium/base/annotations/JNINamespace.java",
+        "base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeMethods.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForM.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForN.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForO.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForOMR1.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForP.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForQ.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForR.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForS.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/DummyJankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
+        "base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/Linker.java",
+        "base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java",
+        "base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java",
+        "base/android/java/src/org/chromium/base/lifetime/DestroyChecker.java",
+        "base/android/java/src/org/chromium/base/lifetime/Destroyable.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPurgeManager.java",
+        "base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordHistogram.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordUserAction.java",
+        "base/android/java/src/org/chromium/base/metrics/ScopedSysTraceEvent.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
+        "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
+        "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
+        "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
+        "base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/OneShotCallback.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/Supplier.java",
+        "base/android/java/src/org/chromium/base/supplier/UnownedUserDataSupplier.java",
+        "base/android/java/src/org/chromium/base/task/AsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/BackgroundOnlyAsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/ChainedTasks.java",
+        "base/android/java/src/org/chromium/base/task/ChoreographerTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java",
+        "base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/SerialExecutor.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraits.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraitsExtensionDescriptor.java",
+        "build/android/java/src/org/chromium/build/annotations/AlwaysInline.java",
+        "build/android/java/src/org/chromium/build/annotations/CheckDiscard.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotClassMerge.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotInline.java",
+        "build/android/java/src/org/chromium/build/annotations/IdentifierNameString.java",
+        "build/android/java/src/org/chromium/build/annotations/MainDex.java",
+        "build/android/java/src/org/chromium/build/annotations/MockedInTests.java",
+        "build/android/java/src/org/chromium/build/annotations/UsedByReflection.java",
+        "components/cronet/android/api/src/org/chromium/net/BidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/CallbackException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalBidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalUrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java",
+        "components/cronet/android/api/src/org/chromium/net/InlineExecutionProhibitedException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityRttListener.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityThroughputListener.java",
+        "components/cronet/android/api/src/org/chromium/net/QuicException.java",
+        "components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataSink.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ByteArrayCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetRequestCompletionListener.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetResponse.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ImplicitFlowControlCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/InMemoryTransformCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/JsonCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandler.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandlers.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/StringCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UrlRequestCallbacks.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetController.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetEngine.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetProvider.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlResponse.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/ResponseMatcher.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/UrlResponseMatcher.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamNetworkException.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLoggerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetManifest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/InputStreamChannel.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngine.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderWithLibraryLoaderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/Preconditions.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UserAgent.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetInputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java",
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/ChromiumNetworkAdapter.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateConstants.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/MimeTypeFilter.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java",
+        "net/android/java/src/org/chromium/net/NetworkTrafficAnnotationTag.java",
+        "net/android/java/src/org/chromium/net/ProxyBroadcastReceiver.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java",
+        "net/android/java/src/org/chromium/net/ThreadStatsUid.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+    ],
+    cmd: "current_dir=`basename \\`pwd\\``; " +
+         "for f in $(in); " +
+         "do " +
+         "echo \"../$$current_dir/$$f\" >> $(genDir)/java.sources; " +
+         "done; " +
+         "python3 $(location base/android/jni_generator/jni_registration_generator.py) --srcjar-path " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.srcjar " +
+         "--depfile " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.d " +
+         "--sources-files " +
+         "$(genDir)/java.sources " +
+         "--include_test_only " +
+         "--use_proxy_hash " +
+         "--header-path " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.h " +
+         "--manual_jni_registration " +
+         ";sed -i -e 's/OUT_SOONG_.TEMP_SBOX_.*_OUT/GEN/g'  " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.h",
+    out: [
+        "components/cronet/android/cronet_jni_registration.h",
+        "components/cronet/android/cronet_jni_registration.srcjar",
+    ],
+    tool_files: [
+        "base/android/jni_generator/jni_generator.py",
+        "base/android/jni_generator/jni_registration_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_jni_registration
+java_genrule {
+    name: "cronet_aml_components_cronet_android_cronet_jni_registration__java",
+    srcs: [
+        "base/android/java/src/org/chromium/base/ActivityState.java",
+        "base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java",
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/ByteArrayGenerator.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CallbackController.java",
+        "base/android/java/src/org/chromium/base/CollectionUtil.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/CommandLineInitUtil.java",
+        "base/android/java/src/org/chromium/base/Consumer.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/ContextUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/DiscardableReferencePool.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/Function.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/IntentUtils.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/JniException.java",
+        "base/android/java/src/org/chromium/base/JniStaticTestMocker.java",
+        "base/android/java/src/org/chromium/base/LifetimeAssert.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/Log.java",
+        "base/android/java/src/org/chromium/base/MathUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
+        "base/android/java/src/org/chromium/base/ObserverList.java",
+        "base/android/java/src/org/chromium/base/PackageManagerUtils.java",
+        "base/android/java/src/org/chromium/base/PackageUtils.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PiiElider.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/PowerMonitorForQ.java",
+        "base/android/java/src/org/chromium/base/Predicate.java",
+        "base/android/java/src/org/chromium/base/Promise.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/StreamUtil.java",
+        "base/android/java/src/org/chromium/base/StrictModeContext.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimeUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/UnownedUserData.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataHost.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataKey.java",
+        "base/android/java/src/org/chromium/base/UserData.java",
+        "base/android/java/src/org/chromium/base/UserDataHost.java",
+        "base/android/java/src/org/chromium/base/WrappedClassLoader.java",
+        "base/android/java/src/org/chromium/base/annotations/AccessedByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeForTesting.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
+        "base/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
+        "base/android/java/src/org/chromium/base/annotations/JNINamespace.java",
+        "base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeMethods.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForM.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForN.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForO.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForOMR1.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForP.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForQ.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForR.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForS.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/DummyJankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
+        "base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/Linker.java",
+        "base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java",
+        "base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java",
+        "base/android/java/src/org/chromium/base/lifetime/DestroyChecker.java",
+        "base/android/java/src/org/chromium/base/lifetime/Destroyable.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPurgeManager.java",
+        "base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordHistogram.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordUserAction.java",
+        "base/android/java/src/org/chromium/base/metrics/ScopedSysTraceEvent.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
+        "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
+        "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
+        "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
+        "base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/OneShotCallback.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/Supplier.java",
+        "base/android/java/src/org/chromium/base/supplier/UnownedUserDataSupplier.java",
+        "base/android/java/src/org/chromium/base/task/AsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/BackgroundOnlyAsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/ChainedTasks.java",
+        "base/android/java/src/org/chromium/base/task/ChoreographerTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java",
+        "base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/SerialExecutor.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraits.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraitsExtensionDescriptor.java",
+        "build/android/java/src/org/chromium/build/annotations/AlwaysInline.java",
+        "build/android/java/src/org/chromium/build/annotations/CheckDiscard.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotClassMerge.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotInline.java",
+        "build/android/java/src/org/chromium/build/annotations/IdentifierNameString.java",
+        "build/android/java/src/org/chromium/build/annotations/MainDex.java",
+        "build/android/java/src/org/chromium/build/annotations/MockedInTests.java",
+        "build/android/java/src/org/chromium/build/annotations/UsedByReflection.java",
+        "components/cronet/android/api/src/org/chromium/net/BidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/CallbackException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalBidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalUrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java",
+        "components/cronet/android/api/src/org/chromium/net/InlineExecutionProhibitedException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityRttListener.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityThroughputListener.java",
+        "components/cronet/android/api/src/org/chromium/net/QuicException.java",
+        "components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataSink.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ByteArrayCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetRequestCompletionListener.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetResponse.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ImplicitFlowControlCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/InMemoryTransformCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/JsonCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandler.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandlers.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/StringCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UrlRequestCallbacks.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetController.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetEngine.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetProvider.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlResponse.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/ResponseMatcher.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/UrlResponseMatcher.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamNetworkException.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLoggerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetManifest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/InputStreamChannel.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngine.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderWithLibraryLoaderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/Preconditions.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UserAgent.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetInputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java",
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/ChromiumNetworkAdapter.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateConstants.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/MimeTypeFilter.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java",
+        "net/android/java/src/org/chromium/net/NetworkTrafficAnnotationTag.java",
+        "net/android/java/src/org/chromium/net/ProxyBroadcastReceiver.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java",
+        "net/android/java/src/org/chromium/net/ThreadStatsUid.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+    ],
+    cmd: "current_dir=`basename \\`pwd\\``; " +
+         "for f in $(in); " +
+         "do " +
+         "echo \"../$$current_dir/$$f\" >> $(genDir)/java.sources; " +
+         "done; " +
+         "python3 $(location base/android/jni_generator/jni_registration_generator.py) --srcjar-path " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.srcjar " +
+         "--depfile " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.d " +
+         "--sources-files " +
+         "$(genDir)/java.sources " +
+         "--include_test_only " +
+         "--use_proxy_hash " +
+         "--header-path " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.h " +
+         "--manual_jni_registration " +
+         ";sed -i -e 's/OUT_SOONG_.TEMP_SBOX_.*_OUT/GEN/g'  " +
+         "$(genDir)/components/cronet/android/cronet_jni_registration.h",
+    out: [
+        "components/cronet/android/cronet_jni_registration.srcjar",
+    ],
+    tool_files: [
+        "base/android/jni_generator/jni_generator.py",
+        "base/android/jni_generator/jni_registration_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+}
+
+// GN: //components/cronet/android:cronet_static
+cc_object {
+    name: "cronet_aml_components_cronet_android_cronet_static",
+    srcs: [
+        "components/cronet/android/cronet_bidirectional_stream_adapter.cc",
+        "components/cronet/android/cronet_context_adapter.cc",
+        "components/cronet/android/cronet_library_loader.cc",
+        "components/cronet/android/cronet_upload_data_stream_adapter.cc",
+        "components/cronet/android/cronet_url_request_adapter.cc",
+        "components/cronet/android/io_buffer_with_byte_buffer.cc",
+        "components/cronet/android/url_request_error.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_components_prefs_prefs",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_net",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_components_cronet_android_cronet_jni_registration",
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "components/cronet/native/generated/",
+        "components/cronet/native/include/",
+        "components/grpc_support/include/",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_buildflags__android_arm",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_buildflags__android_arm64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_arm64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_buildflags__android_x86",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_android_cronet_jni_headers__android_x86_64",
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/android:http_cache_type_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_http_cache_type_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location components/cronet/url_request_context_config.h)",
+    out: [
+        "components/cronet/android/http_cache_type_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "components/cronet/url_request_context_config.h",
+    ],
+}
+
+// GN: //components/cronet/android:implementation_api_version
+java_genrule {
+    name: "cronet_aml_components_cronet_android_implementation_api_version",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-f " +
+         "$(location build/util/LASTCHANGE) " +
+         "-e " +
+         "'API_LEVEL=20' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template)",
+    out: [
+        "components/cronet/android/templates/org/chromium/net/impl/ImplVersion.java",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template",
+    ],
+}
+
+// GN: //components/cronet/android:integrated_mode_state
+genrule {
+    name: "cronet_aml_components_cronet_android_integrated_mode_state",
+    srcs: [
+        ":cronet_aml_components_cronet_android_integrated_mode_state_preprocess",
+    ],
+    tools: [
+        "soong_zip",
+    ],
+    cmd: "cp $(in) $(genDir)/IntegratedModeState.java && " +
+         "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/IntegratedModeState.java",
+    out: [
+        "IntegratedModeState.srcjar",
+    ],
+}
+
+// GN: //components/cronet/android:integrated_mode_state
+cc_object {
+    name: "cronet_aml_components_cronet_android_integrated_mode_state_preprocess",
+    srcs: [
+        ":cronet_aml_components_cronet_android_integrated_mode_state_rename",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-E",
+        "-P",
+    ],
+    compile_multilib: "32",
+}
+
+// GN: //components/cronet/android:integrated_mode_state
+genrule {
+    name: "cronet_aml_components_cronet_android_integrated_mode_state_rename",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/IntegratedModeState.template",
+    ],
+    cmd: "cp $(in) $(out)",
+    out: [
+        "IntegratedModeState.cc",
+    ],
+}
+
+// GN: //components/cronet/android:interface_api_version
+java_genrule {
+    name: "cronet_aml_components_cronet_android_interface_api_version",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-f " +
+         "$(location build/util/LASTCHANGE) " +
+         "-e " +
+         "'API_LEVEL=20' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/android/api/src/org/chromium/net/ApiVersion.template)",
+    out: [
+        "components/cronet/android/templates/org/chromium/net/ApiVersion.java",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/android/api/src/org/chromium/net/ApiVersion.template",
+    ],
+}
+
+// GN: //components/cronet/android:load_states_list
+genrule {
+    name: "cronet_aml_components_cronet_android_load_states_list",
+    srcs: [
+        ":cronet_aml_components_cronet_android_load_states_list_preprocess",
+    ],
+    tools: [
+        "soong_zip",
+    ],
+    cmd: "cp $(in) $(genDir)/LoadState.java && " +
+         "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/LoadState.java",
+    out: [
+        "LoadState.srcjar",
+    ],
+}
+
+// GN: //components/cronet/android:load_states_list
+cc_object {
+    name: "cronet_aml_components_cronet_android_load_states_list_preprocess",
+    srcs: [
+        ":cronet_aml_components_cronet_android_load_states_list_rename",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-E",
+        "-P",
+    ],
+    compile_multilib: "32",
+}
+
+// GN: //components/cronet/android:load_states_list
+genrule {
+    name: "cronet_aml_components_cronet_android_load_states_list_rename",
+    srcs: [
+        "components/cronet/android/java/src/org/chromium/net/impl/LoadState.template",
+    ],
+    cmd: "cp $(in) $(out)",
+    out: [
+        "LoadState.cc",
+    ],
+}
+
+// GN: //components/cronet/android:net_idempotency_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_net_idempotency_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/base/idempotency.h)",
+    out: [
+        "components/cronet/android/net_idempotency_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/base/idempotency.h",
+    ],
+}
+
+// GN: //components/cronet/android:net_request_priority_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_net_request_priority_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/base/request_priority.h)",
+    out: [
+        "components/cronet/android/net_request_priority_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/base/request_priority.h",
+    ],
+}
+
+// GN: //components/cronet/android:network_quality_observation_source_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_network_quality_observation_source_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/nqe/network_quality_observation_source.h)",
+    out: [
+        "components/cronet/android/network_quality_observation_source_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/nqe/network_quality_observation_source.h",
+    ],
+}
+
+// GN: //components/cronet/android:rtt_throughput_values_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_rtt_throughput_values_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/nqe/network_quality.h)",
+    out: [
+        "components/cronet/android/rtt_throughput_values_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/nqe/network_quality.h",
+    ],
+}
+
+// GN: //components/cronet/android:url_request_error_java
+java_genrule {
+    name: "cronet_aml_components_cronet_android_url_request_error_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location components/cronet/android/url_request_error.h)",
+    out: [
+        "components/cronet/android/url_request_error_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "components/cronet/android/url_request_error.h",
+    ],
+}
+
+// GN: //components/cronet:cronet_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+    cmd: "echo '--flags DISABLE_HISTOGRAM_SUPPORT=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet:cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+    cmd: "echo '--flags DISABLE_HISTOGRAM_SUPPORT=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet:cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+    cmd: "echo '--flags DISABLE_HISTOGRAM_SUPPORT=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet:cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+    cmd: "echo '--flags DISABLE_HISTOGRAM_SUPPORT=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/cronet:cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "components/cronet/cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_common
+cc_object {
+    name: "cronet_aml_components_cronet_cronet_common",
+    srcs: [
+        "components/cronet/cronet_context.cc",
+        "components/cronet/cronet_prefs_manager.cc",
+        "components/cronet/cronet_upload_data_stream.cc",
+        "components/cronet/cronet_url_request.cc",
+        "components/cronet/host_cache_persistence_manager.cc",
+        "components/cronet/stale_host_resolver.cc",
+        "components/cronet/url_request_context_config.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_components_prefs_prefs",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_net",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet:cronet_version_header
+cc_object {
+    name: "cronet_aml_components_cronet_cronet_version_header",
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet:cronet_version_header_action__android_arm
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-e " +
+         "'VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/version.h.in)",
+    out: [
+        "components/cronet/version.h",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/version.h.in",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_version_header_action__android_arm64
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-e " +
+         "'VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/version.h.in)",
+    out: [
+        "components/cronet/version.h",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/version.h.in",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_version_header_action__android_x86
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-e " +
+         "'VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/version.h.in)",
+    out: [
+        "components/cronet/version.h",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/version.h.in",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:cronet_version_header_action__android_x86_64
+cc_genrule {
+    name: "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+    cmd: "$(location build/util/version.py) -f " +
+         "$(location chrome/VERSION) " +
+         "-e " +
+         "'VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)' " +
+         "-o " +
+         "$(out) " +
+         "$(location components/cronet/version.h.in)",
+    out: [
+        "components/cronet/version.h",
+    ],
+    tool_files: [
+        "build/util/LASTCHANGE",
+        "build/util/android_chrome_version.py",
+        "build/util/version.py",
+        "chrome/VERSION",
+        "components/cronet/version.h.in",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/cronet:metrics_util
+cc_object {
+    name: "cronet_aml_components_cronet_metrics_util",
+    srcs: [
+        "components/cronet/metrics_util.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/native:cronet_native_headers
+cc_object {
+    name: "cronet_aml_components_cronet_native_cronet_native_headers",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "components/cronet/native/generated/",
+        "components/cronet/native/include/",
+        "components/grpc_support/include/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/cronet/native:cronet_native_impl
+cc_object {
+    name: "cronet_aml_components_cronet_native_cronet_native_impl",
+    srcs: [
+        "components/cronet/native/buffer.cc",
+        "components/cronet/native/engine.cc",
+        "components/cronet/native/generated/cronet.idl_impl_interface.cc",
+        "components/cronet/native/generated/cronet.idl_impl_struct.cc",
+        "components/cronet/native/io_buffer_with_cronet_buffer.cc",
+        "components/cronet/native/native_metrics_util.cc",
+        "components/cronet/native/runnables.cc",
+        "components/cronet/native/upload_data_sink.cc",
+        "components/cronet/native/url_request.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_components_prefs_prefs",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_net",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "components/cronet/native/generated/",
+        "components/cronet/native/include/",
+        "components/grpc_support/include/",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_arm64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_components_cronet_cronet_buildflags__android_x86_64",
+                "cronet_aml_components_cronet_cronet_version_header_action__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //components/grpc_support:grpc_support
+cc_object {
+    name: "cronet_aml_components_grpc_support_grpc_support",
+    srcs: [
+        "components/grpc_support/bidirectional_stream.cc",
+        "components/grpc_support/bidirectional_stream_c.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_net",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/grpc_support:headers
+cc_object {
+    name: "cronet_aml_components_grpc_support_headers",
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/metrics:library_support
+cc_object {
+    name: "cronet_aml_components_metrics_library_support",
+    srcs: [
+        ":cronet_aml_third_party_metrics_proto_metrics_proto_gen",
+        "components/metrics/histogram_encoder.cc",
+        "components/metrics/library_support/histogram_manager.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+    ],
+    generated_headers: [
+        "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //components/nacl/common:buildflags__host
+cc_genrule {
+    name: "cronet_aml_components_nacl_common_buildflags__host",
+    cmd: "echo '--flags ENABLE_NACL=\"true\" IS_MINIMAL_TOOLCHAIN=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//components/nacl/common:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "components/nacl/common/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/prefs/android:jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_components_prefs_android_jni_headers__android_arm",
+    srcs: [
+        "components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/prefs/android/jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "PrefService_jni.h " +
+         "--input_file " +
+         "$(location components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java)",
+    out: [
+        "components/prefs/android/jni_headers/PrefService_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/prefs/android:jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_components_prefs_android_jni_headers__android_arm64",
+    srcs: [
+        "components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/prefs/android/jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "PrefService_jni.h " +
+         "--input_file " +
+         "$(location components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java)",
+    out: [
+        "components/prefs/android/jni_headers/PrefService_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/prefs/android:jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_components_prefs_android_jni_headers__android_x86",
+    srcs: [
+        "components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/prefs/android/jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "PrefService_jni.h " +
+         "--input_file " +
+         "$(location components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java)",
+    out: [
+        "components/prefs/android/jni_headers/PrefService_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/prefs/android:jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_components_prefs_android_jni_headers__android_x86_64",
+    srcs: [
+        "components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/components/prefs/android/jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "PrefService_jni.h " +
+         "--input_file " +
+         "$(location components/prefs/android/java/src/org/chromium/components/prefs/PrefService.java)",
+    out: [
+        "components/prefs/android/jni_headers/PrefService_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //components/prefs:prefs
+cc_library_static {
+    name: "cronet_aml_components_prefs_prefs",
+    srcs: [
+        "components/prefs/android/pref_service_android.cc",
+        "components/prefs/command_line_pref_store.cc",
+        "components/prefs/default_pref_store.cc",
+        "components/prefs/in_memory_pref_store.cc",
+        "components/prefs/json_pref_store.cc",
+        "components/prefs/overlay_user_pref_store.cc",
+        "components/prefs/persistent_pref_store.cc",
+        "components/prefs/pref_change_registrar.cc",
+        "components/prefs/pref_member.cc",
+        "components/prefs/pref_notifier_impl.cc",
+        "components/prefs/pref_registry.cc",
+        "components/prefs/pref_registry_simple.cc",
+        "components/prefs/pref_service.cc",
+        "components/prefs/pref_service_factory.cc",
+        "components/prefs/pref_store.cc",
+        "components/prefs/pref_value_map.cc",
+        "components/prefs/pref_value_store.cc",
+        "components/prefs/scoped_user_pref_update.cc",
+        "components/prefs/segregated_pref_store.cc",
+        "components/prefs/value_map_pref_store.cc",
+        "components/prefs/writeable_pref_store.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCOMPONENTS_PREFS_IMPLEMENTATION",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_components_prefs_android_jni_headers__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_components_prefs_android_jni_headers__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_components_prefs_android_jni_headers__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_components_prefs_android_jni_headers__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_components_prefs_android_jni_headers__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_components_prefs_android_jni_headers__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_components_prefs_android_jni_headers__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_components_prefs_android_jni_headers__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //crypto:buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_crypto_buildflags__android_arm",
+    cmd: "echo '--flags USE_NSS_CERTS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//crypto:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "crypto/crypto_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //crypto:buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_crypto_buildflags__android_arm64",
+    cmd: "echo '--flags USE_NSS_CERTS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//crypto:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "crypto/crypto_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //crypto:buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_crypto_buildflags__android_x86",
+    cmd: "echo '--flags USE_NSS_CERTS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//crypto:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "crypto/crypto_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //crypto:buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_crypto_buildflags__android_x86_64",
+    cmd: "echo '--flags USE_NSS_CERTS=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//crypto:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "crypto/crypto_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //crypto:buildflags__host
+cc_genrule {
+    name: "cronet_aml_crypto_buildflags__host",
+    cmd: "echo '--flags USE_NSS_CERTS=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//crypto:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    host_supported: true,
+    device_supported: false,
+    out: [
+        "crypto/crypto_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //crypto:crypto
+cc_library_static {
+    name: "cronet_aml_crypto_crypto",
+    srcs: [
+        "crypto/aead.cc",
+        "crypto/ec_private_key.cc",
+        "crypto/ec_signature_creator.cc",
+        "crypto/ec_signature_creator_impl.cc",
+        "crypto/encryptor.cc",
+        "crypto/hkdf.cc",
+        "crypto/hmac.cc",
+        "crypto/openssl_util.cc",
+        "crypto/p224_spake.cc",
+        "crypto/random.cc",
+        "crypto/rsa_private_key.cc",
+        "crypto/secure_hash.cc",
+        "crypto/secure_util.cc",
+        "crypto/sha2.cc",
+        "crypto/signature_creator.cc",
+        "crypto/signature_verifier.cc",
+        "crypto/symmetric_key.cc",
+        "crypto/unexportable_key.cc",
+        "crypto/unexportable_key_metrics.cc",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCRYPTO_IMPLEMENTATION",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android: {
+            shared_libs: [
+                "libandroid",
+                "liblog",
+            ],
+        },
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_crypto_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_crypto_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_crypto_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_crypto_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_crypto_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_crypto_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_crypto_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_crypto_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            srcs: [
+                "crypto/nss_key_util.cc",
+                "crypto/nss_util.cc",
+            ],
+            static_libs: [
+                "cronet_aml_base_third_party_symbolize_symbolize",
+                "cronet_aml_base_third_party_xdg_mime_xdg_mime",
+                "cronet_aml_base_third_party_xdg_user_dirs_xdg_user_dirs",
+            ],
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "build/linux/debian_bullseye_amd64-sysroot/usr/include/nspr",
+                "build/linux/debian_bullseye_amd64-sysroot/usr/include/nss",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+                "cronet_aml_components_nacl_common_buildflags__host",
+                "cronet_aml_crypto_buildflags__host",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+                "cronet_aml_components_nacl_common_buildflags__host",
+                "cronet_aml_crypto_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //gn:default_deps
+cc_defaults {
+    name: "cronet_aml_defaults",
+    cflags: [
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-O2",
+        "-Wno-ambiguous-reversed-operator",
+        "-Wno-deprecated-non-prototype",
+        "-Wno-error=return-type",
+        "-Wno-macro-redefined",
+        "-Wno-missing-field-initializers",
+        "-Wno-non-virtual-dtor",
+        "-Wno-null-pointer-subtraction",
+        "-Wno-sign-compare",
+        "-Wno-sign-promo",
+        "-Wno-unreachable-code-loop-increment",
+        "-Wno-unused-parameter",
+        "-fPIC",
+        "-fvisibility=hidden",
+    ],
+    stl: "none",
+    apex_available: [
+        "com.android.tethering",
+    ],
+    min_sdk_version: "29",
+    target: {
+        android: {
+            header_libs: [
+                "jni_headers",
+                "media_ndk_headers",
+            ],
+        },
+        host: {
+            cflags: [
+                "-UANDROID",
+            ],
+        },
+    },
+}
+
+// GN: //ipc:param_traits
+cc_object {
+    name: "cronet_aml_ipc_param_traits",
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //gn:java
+java_library {
+    name: "cronet_aml_java",
+    srcs: [
+        ":cronet_aml_base_base_android_java_enums_srcjar",
+        ":cronet_aml_base_java_features_srcjar",
+        ":cronet_aml_base_java_switches_srcjar",
+        ":cronet_aml_build_android_build_config_gen",
+        ":cronet_aml_build_android_native_libraries_gen",
+        ":cronet_aml_components_cronet_android_cronet_jni_registration__java",
+        ":cronet_aml_components_cronet_android_http_cache_type_java",
+        ":cronet_aml_components_cronet_android_implementation_api_version",
+        ":cronet_aml_components_cronet_android_integrated_mode_state",
+        ":cronet_aml_components_cronet_android_interface_api_version",
+        ":cronet_aml_components_cronet_android_load_states_list",
+        ":cronet_aml_components_cronet_android_net_idempotency_java",
+        ":cronet_aml_components_cronet_android_net_request_priority_java",
+        ":cronet_aml_components_cronet_android_network_quality_observation_source_java",
+        ":cronet_aml_components_cronet_android_rtt_throughput_values_java",
+        ":cronet_aml_components_cronet_android_url_request_error_java",
+        ":cronet_aml_net_android_net_android_java_enums_srcjar",
+        ":cronet_aml_net_android_net_errors_java",
+        ":cronet_aml_net_effective_connection_type_java",
+        "base/android/java/src/org/chromium/base/ActivityState.java",
+        "base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java",
+        "base/android/java/src/org/chromium/base/ApkAssets.java",
+        "base/android/java/src/org/chromium/base/ApplicationStatus.java",
+        "base/android/java/src/org/chromium/base/BaseFeatureList.java",
+        "base/android/java/src/org/chromium/base/BuildInfo.java",
+        "base/android/java/src/org/chromium/base/BundleUtils.java",
+        "base/android/java/src/org/chromium/base/ByteArrayGenerator.java",
+        "base/android/java/src/org/chromium/base/Callback.java",
+        "base/android/java/src/org/chromium/base/CallbackController.java",
+        "base/android/java/src/org/chromium/base/CollectionUtil.java",
+        "base/android/java/src/org/chromium/base/CommandLine.java",
+        "base/android/java/src/org/chromium/base/CommandLineInitUtil.java",
+        "base/android/java/src/org/chromium/base/Consumer.java",
+        "base/android/java/src/org/chromium/base/ContentUriUtils.java",
+        "base/android/java/src/org/chromium/base/ContextUtils.java",
+        "base/android/java/src/org/chromium/base/CpuFeatures.java",
+        "base/android/java/src/org/chromium/base/DiscardableReferencePool.java",
+        "base/android/java/src/org/chromium/base/EarlyTraceEvent.java",
+        "base/android/java/src/org/chromium/base/EventLog.java",
+        "base/android/java/src/org/chromium/base/FeatureList.java",
+        "base/android/java/src/org/chromium/base/Features.java",
+        "base/android/java/src/org/chromium/base/FieldTrialList.java",
+        "base/android/java/src/org/chromium/base/FileUtils.java",
+        "base/android/java/src/org/chromium/base/Function.java",
+        "base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+        "base/android/java/src/org/chromium/base/IntStringCallback.java",
+        "base/android/java/src/org/chromium/base/IntentUtils.java",
+        "base/android/java/src/org/chromium/base/JNIUtils.java",
+        "base/android/java/src/org/chromium/base/JavaExceptionReporter.java",
+        "base/android/java/src/org/chromium/base/JavaHandlerThread.java",
+        "base/android/java/src/org/chromium/base/JniException.java",
+        "base/android/java/src/org/chromium/base/JniStaticTestMocker.java",
+        "base/android/java/src/org/chromium/base/LifetimeAssert.java",
+        "base/android/java/src/org/chromium/base/LocaleUtils.java",
+        "base/android/java/src/org/chromium/base/Log.java",
+        "base/android/java/src/org/chromium/base/MathUtils.java",
+        "base/android/java/src/org/chromium/base/MemoryPressureListener.java",
+        "base/android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
+        "base/android/java/src/org/chromium/base/ObserverList.java",
+        "base/android/java/src/org/chromium/base/PackageManagerUtils.java",
+        "base/android/java/src/org/chromium/base/PackageUtils.java",
+        "base/android/java/src/org/chromium/base/PathService.java",
+        "base/android/java/src/org/chromium/base/PathUtils.java",
+        "base/android/java/src/org/chromium/base/PiiElider.java",
+        "base/android/java/src/org/chromium/base/PowerMonitor.java",
+        "base/android/java/src/org/chromium/base/PowerMonitorForQ.java",
+        "base/android/java/src/org/chromium/base/Predicate.java",
+        "base/android/java/src/org/chromium/base/Promise.java",
+        "base/android/java/src/org/chromium/base/RadioUtils.java",
+        "base/android/java/src/org/chromium/base/StreamUtil.java",
+        "base/android/java/src/org/chromium/base/StrictModeContext.java",
+        "base/android/java/src/org/chromium/base/SysUtils.java",
+        "base/android/java/src/org/chromium/base/ThreadUtils.java",
+        "base/android/java/src/org/chromium/base/TimeUtils.java",
+        "base/android/java/src/org/chromium/base/TimezoneUtils.java",
+        "base/android/java/src/org/chromium/base/TraceEvent.java",
+        "base/android/java/src/org/chromium/base/UnguessableToken.java",
+        "base/android/java/src/org/chromium/base/UnownedUserData.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataHost.java",
+        "base/android/java/src/org/chromium/base/UnownedUserDataKey.java",
+        "base/android/java/src/org/chromium/base/UserData.java",
+        "base/android/java/src/org/chromium/base/UserDataHost.java",
+        "base/android/java/src/org/chromium/base/WrappedClassLoader.java",
+        "base/android/java/src/org/chromium/base/annotations/AccessedByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNative.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeForTesting.java",
+        "base/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
+        "base/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
+        "base/android/java/src/org/chromium/base/annotations/JNINamespace.java",
+        "base/android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
+        "base/android/java/src/org/chromium/base/annotations/NativeMethods.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForM.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForN.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForO.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForOMR1.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForP.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForQ.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForR.java",
+        "base/android/java/src/org/chromium/base/compat/ApiHelperForS.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/DummyJankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
+        "base/android/java/src/org/chromium/base/jank_tracker/JankTrackerImpl.java",
+        "base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+        "base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+        "base/android/java/src/org/chromium/base/library_loader/Linker.java",
+        "base/android/java/src/org/chromium/base/library_loader/LinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
+        "base/android/java/src/org/chromium/base/library_loader/ModernLinkerJni.java",
+        "base/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java",
+        "base/android/java/src/org/chromium/base/library_loader/ProcessInitException.java",
+        "base/android/java/src/org/chromium/base/lifetime/DestroyChecker.java",
+        "base/android/java/src/org/chromium/base/lifetime/Destroyable.java",
+        "base/android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
+        "base/android/java/src/org/chromium/base/memory/MemoryPurgeManager.java",
+        "base/android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordHistogram.java",
+        "base/android/java/src/org/chromium/base/metrics/RecordUserAction.java",
+        "base/android/java/src/org/chromium/base/metrics/ScopedSysTraceEvent.java",
+        "base/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
+        "base/android/java/src/org/chromium/base/metrics/TimingMetric.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorder.java",
+        "base/android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java",
+        "base/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java",
+        "base/android/java/src/org/chromium/base/process_launcher/BindService.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java",
+        "base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java",
+        "base/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
+        "base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl",
+        "base/android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl",
+        "base/android/java/src/org/chromium/base/supplier/BooleanSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/OneShotCallback.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplier.java",
+        "base/android/java/src/org/chromium/base/supplier/OneshotSupplierImpl.java",
+        "base/android/java/src/org/chromium/base/supplier/Supplier.java",
+        "base/android/java/src/org/chromium/base/supplier/UnownedUserDataSupplier.java",
+        "base/android/java/src/org/chromium/base/task/AsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/BackgroundOnlyAsyncTask.java",
+        "base/android/java/src/org/chromium/base/task/ChainedTasks.java",
+        "base/android/java/src/org/chromium/base/task/ChoreographerTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java",
+        "base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/PostTask.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/SerialExecutor.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskExecutor.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunner.java",
+        "base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraits.java",
+        "base/android/java/src/org/chromium/base/task/TaskTraitsExtensionDescriptor.java",
+        "build/android/java/src/org/chromium/build/annotations/AlwaysInline.java",
+        "build/android/java/src/org/chromium/build/annotations/CheckDiscard.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotClassMerge.java",
+        "build/android/java/src/org/chromium/build/annotations/DoNotInline.java",
+        "build/android/java/src/org/chromium/build/annotations/IdentifierNameString.java",
+        "build/android/java/src/org/chromium/build/annotations/MainDex.java",
+        "build/android/java/src/org/chromium/build/annotations/MockedInTests.java",
+        "build/android/java/src/org/chromium/build/annotations/UsedByReflection.java",
+        "components/cronet/android/api/src/org/chromium/net/BidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/CallbackException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetException.java",
+        "components/cronet/android/api/src/org/chromium/net/CronetProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalBidirectionalStream.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java",
+        "components/cronet/android/api/src/org/chromium/net/ExperimentalUrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java",
+        "components/cronet/android/api/src/org/chromium/net/InlineExecutionProhibitedException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkException.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityRttListener.java",
+        "components/cronet/android/api/src/org/chromium/net/NetworkQualityThroughputListener.java",
+        "components/cronet/android/api/src/org/chromium/net/QuicException.java",
+        "components/cronet/android/api/src/org/chromium/net/RequestFinishedInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProvider.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/UploadDataSink.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlRequest.java",
+        "components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ByteArrayCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ContentTypeParametersParser.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetRequestCompletionListener.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/CronetResponse.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/ImplicitFlowControlCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/InMemoryTransformCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/JsonCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandler.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/RedirectHandlers.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/StringCronetCallback.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java",
+        "components/cronet/android/api/src/org/chromium/net/apihelpers/UrlRequestCallbacks.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetController.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetEngine.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeCronetProvider.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlRequest.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/FakeUrlResponse.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/ResponseMatcher.java",
+        "components/cronet/android/fake/java/org/chromium/net/test/UrlResponseMatcher.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/BidirectionalStreamNetworkException.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CallbackExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetLoggerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetManifest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetMetrics.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/InputStreamChannel.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngine.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUploadDataSinkBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequest.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/JavaUrlRequestUtils.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetEngineBuilderWithLibraryLoaderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NativeCronetProvider.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NetworkExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/NoOpLogger.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/Preconditions.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/QuicExceptionImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/RequestFinishedInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBase.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlRequestBuilderImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UrlResponseInfoImpl.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/UserAgent.java",
+        "components/cronet/android/java/src/org/chromium/net/impl/VersionSafeCallbacks.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandler.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetInputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactory.java",
+        "components/cronet/android/java/src/org/chromium/net/urlconnection/MessageLoop.java",
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/ChromiumNetworkAdapter.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateConstants.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/MimeTypeFilter.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java",
+        "net/android/java/src/org/chromium/net/NetworkTrafficAnnotationTag.java",
+        "net/android/java/src/org/chromium/net/ProxyBroadcastReceiver.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java",
+        "net/android/java/src/org/chromium/net/RegistrationPolicyApplicationStatus.java",
+        "net/android/java/src/org/chromium/net/ThreadStatsUid.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+    ],
+}
+
+// GN: //net/android:net_android_java_enums_srcjar
+java_genrule {
+    name: "cronet_aml_net_android_net_android_java_enums_srcjar",
+    srcs: [
+        "net/android/network_change_notifier_android.cc",
+        "net/android/traffic_stats.cc",
+        "net/socket/socket_tag.cc",
+    ],
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/base/network_change_notifier.h) " +
+         "$(location net/socket/socket_tag.cc) " +
+         "$(location net/android/cert_verify_result_android.h) " +
+         "$(location net/android/keystore.h) " +
+         "$(location net/android/network_change_notifier_android.cc) " +
+         "$(location net/android/traffic_stats.cc)",
+    out: [
+        "net/android/net_android_java_enums_srcjar.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/android/cert_verify_result_android.h",
+        "net/android/keystore.h",
+        "net/base/network_change_notifier.h",
+    ],
+}
+
+// GN: //net/android:net_errors_java
+genrule {
+    name: "cronet_aml_net_android_net_errors_java",
+    srcs: [
+        ":cronet_aml_net_android_net_errors_java_preprocess",
+    ],
+    tools: [
+        "soong_zip",
+    ],
+    cmd: "cp $(in) $(genDir)/NetError.java && " +
+         "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/NetError.java",
+    out: [
+        "NetError.srcjar",
+    ],
+}
+
+// GN: //net/android:net_errors_java
+cc_object {
+    name: "cronet_aml_net_android_net_errors_java_preprocess",
+    srcs: [
+        ":cronet_aml_net_android_net_errors_java_rename",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-E",
+        "-P",
+    ],
+    compile_multilib: "32",
+}
+
+// GN: //net/android:net_errors_java
+genrule {
+    name: "cronet_aml_net_android_net_errors_java_rename",
+    srcs: [
+        "net/android/java/NetError.template",
+    ],
+    cmd: "cp $(in) $(out)",
+    out: [
+        "NetError.cc",
+    ],
+}
+
+// GN: //net/base/registry_controlled_domains:registry_controlled_domains__android_arm
+cc_genrule {
+    name: "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+    cmd: "$(location net/tools/dafsa/make_dafsa.py) --reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc)",
+    out: [
+        "net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc",
+    ],
+    tool_files: [
+        "net/base/registry_controlled_domains/effective_tld_names.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf",
+        "net/tools/dafsa/make_dafsa.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/base/registry_controlled_domains:registry_controlled_domains__android_arm64
+cc_genrule {
+    name: "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+    cmd: "$(location net/tools/dafsa/make_dafsa.py) --reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc)",
+    out: [
+        "net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc",
+    ],
+    tool_files: [
+        "net/base/registry_controlled_domains/effective_tld_names.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf",
+        "net/tools/dafsa/make_dafsa.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/base/registry_controlled_domains:registry_controlled_domains__android_x86
+cc_genrule {
+    name: "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+    cmd: "$(location net/tools/dafsa/make_dafsa.py) --reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc)",
+    out: [
+        "net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc",
+    ],
+    tool_files: [
+        "net/base/registry_controlled_domains/effective_tld_names.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf",
+        "net/tools/dafsa/make_dafsa.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/base/registry_controlled_domains:registry_controlled_domains__android_x86_64
+cc_genrule {
+    name: "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+    cmd: "$(location net/tools/dafsa/make_dafsa.py) --reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc) " +
+         "&& python3 $(location net/tools/dafsa/make_dafsa.py) " +
+         "--reverse " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf) " +
+         "$(location net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc)",
+    out: [
+        "net/base/registry_controlled_domains/effective_tld_names-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5-reversed-inc.cc",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6-reversed-inc.cc",
+    ],
+    tool_files: [
+        "net/base/registry_controlled_domains/effective_tld_names.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest1.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest2.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest3.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest4.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest5.gperf",
+        "net/base/registry_controlled_domains/effective_tld_names_unittest6.gperf",
+        "net/tools/dafsa/make_dafsa.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_net_buildflags__android_arm",
+    cmd: "echo '--flags POSIX_BYPASS_MMAP=\"true\" DISABLE_FILE_SUPPORT=\"true\" ENABLE_MDNS=\"false\" ENABLE_REPORTING=\"true\" ENABLE_WEBSOCKETS=\"false\" INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST=\"false\" USE_KERBEROS=\"true\" USE_EXTERNAL_GSSAPI=\"false\" TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=\"false\" CHROME_ROOT_STORE_SUPPORTED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/net_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_net_buildflags__android_arm64",
+    cmd: "echo '--flags POSIX_BYPASS_MMAP=\"true\" DISABLE_FILE_SUPPORT=\"true\" ENABLE_MDNS=\"false\" ENABLE_REPORTING=\"true\" ENABLE_WEBSOCKETS=\"false\" INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST=\"false\" USE_KERBEROS=\"true\" USE_EXTERNAL_GSSAPI=\"false\" TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=\"false\" CHROME_ROOT_STORE_SUPPORTED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/net_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_net_buildflags__android_x86",
+    cmd: "echo '--flags POSIX_BYPASS_MMAP=\"false\" DISABLE_FILE_SUPPORT=\"true\" ENABLE_MDNS=\"false\" ENABLE_REPORTING=\"true\" ENABLE_WEBSOCKETS=\"false\" INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST=\"false\" USE_KERBEROS=\"true\" USE_EXTERNAL_GSSAPI=\"false\" TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=\"false\" CHROME_ROOT_STORE_SUPPORTED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/net_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_net_buildflags__android_x86_64",
+    cmd: "echo '--flags POSIX_BYPASS_MMAP=\"true\" DISABLE_FILE_SUPPORT=\"true\" ENABLE_MDNS=\"false\" ENABLE_REPORTING=\"true\" ENABLE_WEBSOCKETS=\"false\" INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST=\"false\" USE_KERBEROS=\"true\" USE_EXTERNAL_GSSAPI=\"false\" TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=\"false\" CHROME_ROOT_STORE_SUPPORTED=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/net_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:constants
+cc_object {
+    name: "cronet_aml_net_constants",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //net/data/ssl/chrome_root_store:gen_root_store_inc__android_arm
+cc_genrule {
+    name: "cronet_aml_net_data_ssl_chrome_root_store_gen_root_store_inc__android_arm",
+    cmd: "$(location build/gn_run_binary.py) clang_x64/root_store_tool " +
+         "--root-store " +
+         "../../net/data/ssl/chrome_root_store/root_store.textproto " +
+         "--certs " +
+         "../../net/data/ssl/chrome_root_store/root_store.certs " +
+         "--write-cpp-root-store " +
+         "gen/net/data/ssl/chrome_root_store/chrome-root-store-inc.cc " +
+         "--write-cpp-ev-roots " +
+         "gen/net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+    out: [
+        "net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+        "net/data/ssl/chrome_root_store/chrome-root-store-inc.cc",
+    ],
+    tool_files: [
+        "build/gn_run_binary.py",
+        "net/data/ssl/chrome_root_store/root_store.certs",
+        "net/data/ssl/chrome_root_store/root_store.textproto",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/data/ssl/chrome_root_store:gen_root_store_inc__android_arm64
+cc_genrule {
+    name: "cronet_aml_net_data_ssl_chrome_root_store_gen_root_store_inc__android_arm64",
+    cmd: "$(location build/gn_run_binary.py) clang_x64/root_store_tool " +
+         "--root-store " +
+         "../../net/data/ssl/chrome_root_store/root_store.textproto " +
+         "--certs " +
+         "../../net/data/ssl/chrome_root_store/root_store.certs " +
+         "--write-cpp-root-store " +
+         "gen/net/data/ssl/chrome_root_store/chrome-root-store-inc.cc " +
+         "--write-cpp-ev-roots " +
+         "gen/net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+    out: [
+        "net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+        "net/data/ssl/chrome_root_store/chrome-root-store-inc.cc",
+    ],
+    tool_files: [
+        "build/gn_run_binary.py",
+        "net/data/ssl/chrome_root_store/root_store.certs",
+        "net/data/ssl/chrome_root_store/root_store.textproto",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/data/ssl/chrome_root_store:gen_root_store_inc__android_x86
+cc_genrule {
+    name: "cronet_aml_net_data_ssl_chrome_root_store_gen_root_store_inc__android_x86",
+    cmd: "$(location build/gn_run_binary.py) clang_x64/root_store_tool " +
+         "--root-store " +
+         "../../net/data/ssl/chrome_root_store/root_store.textproto " +
+         "--certs " +
+         "../../net/data/ssl/chrome_root_store/root_store.certs " +
+         "--write-cpp-root-store " +
+         "gen/net/data/ssl/chrome_root_store/chrome-root-store-inc.cc " +
+         "--write-cpp-ev-roots " +
+         "gen/net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+    out: [
+        "net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+        "net/data/ssl/chrome_root_store/chrome-root-store-inc.cc",
+    ],
+    tool_files: [
+        "build/gn_run_binary.py",
+        "net/data/ssl/chrome_root_store/root_store.certs",
+        "net/data/ssl/chrome_root_store/root_store.textproto",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/data/ssl/chrome_root_store:gen_root_store_inc__android_x86_64
+cc_genrule {
+    name: "cronet_aml_net_data_ssl_chrome_root_store_gen_root_store_inc__android_x86_64",
+    cmd: "$(location build/gn_run_binary.py) clang_x64/root_store_tool " +
+         "--root-store " +
+         "../../net/data/ssl/chrome_root_store/root_store.textproto " +
+         "--certs " +
+         "../../net/data/ssl/chrome_root_store/root_store.certs " +
+         "--write-cpp-root-store " +
+         "gen/net/data/ssl/chrome_root_store/chrome-root-store-inc.cc " +
+         "--write-cpp-ev-roots " +
+         "gen/net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+    out: [
+        "net/data/ssl/chrome_root_store/chrome-ev-roots-inc.cc",
+        "net/data/ssl/chrome_root_store/chrome-root-store-inc.cc",
+    ],
+    tool_files: [
+        "build/gn_run_binary.py",
+        "net/data/ssl/chrome_root_store/root_store.certs",
+        "net/data/ssl/chrome_root_store/root_store.textproto",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/dns:dns
+cc_object {
+    name: "cronet_aml_net_dns_dns",
+    srcs: [
+        "net/dns/address_info.cc",
+        "net/dns/address_sorter_posix.cc",
+        "net/dns/context_host_resolver.cc",
+        "net/dns/dns_alias_utility.cc",
+        "net/dns/dns_client.cc",
+        "net/dns/dns_config.cc",
+        "net/dns/dns_config_service.cc",
+        "net/dns/dns_config_service_android.cc",
+        "net/dns/dns_hosts.cc",
+        "net/dns/dns_query.cc",
+        "net/dns/dns_reloader.cc",
+        "net/dns/dns_response.cc",
+        "net/dns/dns_response_result_extractor.cc",
+        "net/dns/dns_server_iterator.cc",
+        "net/dns/dns_session.cc",
+        "net/dns/dns_transaction.cc",
+        "net/dns/dns_udp_tracker.cc",
+        "net/dns/dns_util.cc",
+        "net/dns/host_cache.cc",
+        "net/dns/host_resolver.cc",
+        "net/dns/host_resolver_manager.cc",
+        "net/dns/host_resolver_mdns_listener_impl.cc",
+        "net/dns/host_resolver_mdns_task.cc",
+        "net/dns/host_resolver_nat64_task.cc",
+        "net/dns/host_resolver_proc.cc",
+        "net/dns/host_resolver_system_task.cc",
+        "net/dns/https_record_rdata.cc",
+        "net/dns/httpssvc_metrics.cc",
+        "net/dns/mapped_host_resolver.cc",
+        "net/dns/nsswitch_reader.cc",
+        "net/dns/opt_record_rdata.cc",
+        "net/dns/record_parsed.cc",
+        "net/dns/record_rdata.cc",
+        "net/dns/resolve_context.cc",
+        "net/dns/serial_worker.cc",
+        "net/dns/system_dns_config_change_notifier.cc",
+        "net/dns/test_dns_config_service.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/dns:dns_client
+cc_object {
+    name: "cronet_aml_net_dns_dns_client",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/dns:host_resolver
+cc_object {
+    name: "cronet_aml_net_dns_host_resolver",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/dns:host_resolver_manager
+cc_object {
+    name: "cronet_aml_net_dns_host_resolver_manager",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/dns:mdns_client
+cc_object {
+    name: "cronet_aml_net_dns_mdns_client",
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/dns/public:public
+cc_object {
+    name: "cronet_aml_net_dns_public_public",
+    srcs: [
+        "net/dns/public/dns_config_overrides.cc",
+        "net/dns/public/dns_over_https_config.cc",
+        "net/dns/public/dns_over_https_server_config.cc",
+        "net/dns/public/dns_query_type.cc",
+        "net/dns/public/doh_provider_entry.cc",
+        "net/dns/public/host_resolver_results.cc",
+        "net/dns/public/resolve_error_info.cc",
+        "net/dns/public/util.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:effective_connection_type_java
+java_genrule {
+    name: "cronet_aml_net_effective_connection_type_java",
+    cmd: "$(location build/android/gyp/java_cpp_enum.py) --srcjar " +
+         "$(out) " +
+         "$(location net/nqe/effective_connection_type.h)",
+    out: [
+        "net/effective_connection_type_java.srcjar",
+    ],
+    tool_files: [
+        "build/android/gyp/java_cpp_enum.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/android/gyp/util/java_cpp_utils.py",
+        "build/gn_helpers.py",
+        "net/nqe/effective_connection_type.h",
+    ],
+}
+
+// GN: //net/http:transport_security_state_generated_files
+cc_object {
+    name: "cronet_aml_net_http_transport_security_state_generated_files",
+    srcs: [
+        "net/http/transport_security_state.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_branding_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_branding_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_branding_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_branding_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:ios_cronet_buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_net_ios_cronet_buildflags__android_arm",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/socket/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:ios_cronet_buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_net_ios_cronet_buildflags__android_arm64",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/socket/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:ios_cronet_buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_net_ios_cronet_buildflags__android_x86",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/socket/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:ios_cronet_buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_net_ios_cronet_buildflags__android_x86_64",
+    cmd: "echo '--flags CRONET_BUILD=\"false\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//net:ios_cronet_buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "net/socket/ios_cronet_buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:isolation_info_proto
+cc_genrule {
+    name: "cronet_aml_net_isolation_info_proto_gen",
+    srcs: [
+        "net/base/isolation_info.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/base --cpp_out=lite=true:$(genDir)/external/chromium_org/net/base/ $(in)",
+    out: [
+        "external/chromium_org/net/base/isolation_info.pb.cc",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:isolation_info_proto
+cc_genrule {
+    name: "cronet_aml_net_isolation_info_proto_gen_headers",
+    srcs: [
+        "net/base/isolation_info.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/base --cpp_out=lite=true:$(genDir)/external/chromium_org/net/base/ $(in)",
+    out: [
+        "external/chromium_org/net/base/isolation_info.pb.h",
+    ],
+    export_include_dirs: [
+        ".",
+        "net/base",
+        "protos",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net
+cc_library_static {
+    name: "cronet_aml_net_net",
+    srcs: [
+        ":cronet_aml_net_dns_dns",
+        ":cronet_aml_net_dns_public_public",
+        ":cronet_aml_net_http_transport_security_state_generated_files",
+        ":cronet_aml_net_net_deps",
+        ":cronet_aml_net_net_public_deps",
+        ":cronet_aml_net_traffic_annotation_traffic_annotation",
+        "net/android/android_http_util.cc",
+        "net/android/cert_verify_result_android.cc",
+        "net/android/gurl_utils.cc",
+        "net/android/http_auth_negotiate_android.cc",
+        "net/android/keystore.cc",
+        "net/android/network_change_notifier_android.cc",
+        "net/android/network_change_notifier_delegate_android.cc",
+        "net/android/network_change_notifier_factory_android.cc",
+        "net/android/network_library.cc",
+        "net/android/radio_activity_tracker.cc",
+        "net/android/traffic_stats.cc",
+        "net/base/address_family.cc",
+        "net/base/address_list.cc",
+        "net/base/address_tracker_linux.cc",
+        "net/base/auth.cc",
+        "net/base/backoff_entry.cc",
+        "net/base/backoff_entry_serializer.cc",
+        "net/base/cache_metrics.cc",
+        "net/base/chunked_upload_data_stream.cc",
+        "net/base/connection_endpoint_metadata.cc",
+        "net/base/data_url.cc",
+        "net/base/datagram_buffer.cc",
+        "net/base/elements_upload_data_stream.cc",
+        "net/base/features.cc",
+        "net/base/file_stream.cc",
+        "net/base/file_stream_context.cc",
+        "net/base/file_stream_context_posix.cc",
+        "net/base/filename_util.cc",
+        "net/base/filename_util_internal.cc",
+        "net/base/hash_value.cc",
+        "net/base/hex_utils.cc",
+        "net/base/host_mapping_rules.cc",
+        "net/base/host_port_pair.cc",
+        "net/base/io_buffer.cc",
+        "net/base/ip_address.cc",
+        "net/base/ip_endpoint.cc",
+        "net/base/isolation_info.cc",
+        "net/base/load_timing_info.cc",
+        "net/base/logging_network_change_observer.cc",
+        "net/base/lookup_string_in_fixed_set.cc",
+        "net/base/mime_sniffer.cc",
+        "net/base/mime_util.cc",
+        "net/base/net_errors.cc",
+        "net/base/net_errors_posix.cc",
+        "net/base/net_module.cc",
+        "net/base/net_string_util_icu_alternatives_android.cc",
+        "net/base/network_activity_monitor.cc",
+        "net/base/network_anonymization_key.cc",
+        "net/base/network_change_notifier.cc",
+        "net/base/network_change_notifier_posix.cc",
+        "net/base/network_delegate.cc",
+        "net/base/network_delegate_impl.cc",
+        "net/base/network_interfaces.cc",
+        "net/base/network_interfaces_getifaddrs.cc",
+        "net/base/network_interfaces_getifaddrs_android.cc",
+        "net/base/network_interfaces_linux.cc",
+        "net/base/network_interfaces_posix.cc",
+        "net/base/network_isolation_key.cc",
+        "net/base/parse_number.cc",
+        "net/base/platform_mime_util_linux.cc",
+        "net/base/port_util.cc",
+        "net/base/prioritized_dispatcher.cc",
+        "net/base/prioritized_task_runner.cc",
+        "net/base/privacy_mode.cc",
+        "net/base/proxy_server.cc",
+        "net/base/proxy_string_util.cc",
+        "net/base/registry_controlled_domains/registry_controlled_domain.cc",
+        "net/base/request_priority.cc",
+        "net/base/scheme_host_port_matcher.cc",
+        "net/base/scheme_host_port_matcher_rule.cc",
+        "net/base/schemeful_site.cc",
+        "net/base/sockaddr_storage.cc",
+        "net/base/sockaddr_util_posix.cc",
+        "net/base/transport_info.cc",
+        "net/base/upload_bytes_element_reader.cc",
+        "net/base/upload_data_stream.cc",
+        "net/base/upload_element_reader.cc",
+        "net/base/upload_file_element_reader.cc",
+        "net/base/url_util.cc",
+        "net/cert/asn1_util.cc",
+        "net/cert/caching_cert_verifier.cc",
+        "net/cert/cert_and_ct_verifier.cc",
+        "net/cert/cert_database.cc",
+        "net/cert/cert_status_flags.cc",
+        "net/cert/cert_verifier.cc",
+        "net/cert/cert_verify_proc.cc",
+        "net/cert/cert_verify_proc_android.cc",
+        "net/cert/cert_verify_proc_builtin.cc",
+        "net/cert/cert_verify_result.cc",
+        "net/cert/coalescing_cert_verifier.cc",
+        "net/cert/crl_set.cc",
+        "net/cert/ct_log_response_parser.cc",
+        "net/cert/ct_log_verifier.cc",
+        "net/cert/ct_log_verifier_util.cc",
+        "net/cert/ct_objects_extractor.cc",
+        "net/cert/ct_policy_enforcer.cc",
+        "net/cert/ct_sct_to_string.cc",
+        "net/cert/ct_serialization.cc",
+        "net/cert/ct_signed_certificate_timestamp_log_param.cc",
+        "net/cert/do_nothing_ct_verifier.cc",
+        "net/cert/ev_root_ca_metadata.cc",
+        "net/cert/internal/cert_issuer_source_aia.cc",
+        "net/cert/internal/revocation_checker.cc",
+        "net/cert/internal/system_trust_store.cc",
+        "net/cert/known_roots.cc",
+        "net/cert/merkle_audit_proof.cc",
+        "net/cert/merkle_consistency_proof.cc",
+        "net/cert/merkle_tree_leaf.cc",
+        "net/cert/multi_log_ct_verifier.cc",
+        "net/cert/multi_threaded_cert_verifier.cc",
+        "net/cert/ocsp_verify_result.cc",
+        "net/cert/pem.cc",
+        "net/cert/pki/cert_error_id.cc",
+        "net/cert/pki/cert_error_params.cc",
+        "net/cert/pki/cert_errors.cc",
+        "net/cert/pki/cert_issuer_source_static.cc",
+        "net/cert/pki/certificate_policies.cc",
+        "net/cert/pki/common_cert_errors.cc",
+        "net/cert/pki/crl.cc",
+        "net/cert/pki/extended_key_usage.cc",
+        "net/cert/pki/general_names.cc",
+        "net/cert/pki/name_constraints.cc",
+        "net/cert/pki/ocsp.cc",
+        "net/cert/pki/parse_certificate.cc",
+        "net/cert/pki/parse_name.cc",
+        "net/cert/pki/parsed_certificate.cc",
+        "net/cert/pki/path_builder.cc",
+        "net/cert/pki/revocation_util.cc",
+        "net/cert/pki/signature_algorithm.cc",
+        "net/cert/pki/simple_path_builder_delegate.cc",
+        "net/cert/pki/string_util.cc",
+        "net/cert/pki/trust_store.cc",
+        "net/cert/pki/trust_store_collection.cc",
+        "net/cert/pki/trust_store_in_memory.cc",
+        "net/cert/pki/verify_certificate_chain.cc",
+        "net/cert/pki/verify_name_match.cc",
+        "net/cert/pki/verify_signed_data.cc",
+        "net/cert/sct_status_flags.cc",
+        "net/cert/signed_certificate_timestamp.cc",
+        "net/cert/signed_certificate_timestamp_and_status.cc",
+        "net/cert/signed_tree_head.cc",
+        "net/cert/symantec_certs.cc",
+        "net/cert/test_root_certs.cc",
+        "net/cert/test_root_certs_android.cc",
+        "net/cert/trial_comparison_cert_verifier_util.cc",
+        "net/cert/x509_cert_types.cc",
+        "net/cert/x509_certificate.cc",
+        "net/cert/x509_certificate_net_log_param.cc",
+        "net/cert/x509_util.cc",
+        "net/cert/x509_util_android.cc",
+        "net/cert_net/cert_net_fetcher_url_request.cc",
+        "net/cookies/canonical_cookie.cc",
+        "net/cookies/cookie_access_delegate.cc",
+        "net/cookies/cookie_access_result.cc",
+        "net/cookies/cookie_change_dispatcher.cc",
+        "net/cookies/cookie_constants.cc",
+        "net/cookies/cookie_deletion_info.cc",
+        "net/cookies/cookie_inclusion_status.cc",
+        "net/cookies/cookie_monster.cc",
+        "net/cookies/cookie_monster_change_dispatcher.cc",
+        "net/cookies/cookie_monster_netlog_params.cc",
+        "net/cookies/cookie_options.cc",
+        "net/cookies/cookie_partition_key.cc",
+        "net/cookies/cookie_partition_key_collection.cc",
+        "net/cookies/cookie_store.cc",
+        "net/cookies/cookie_util.cc",
+        "net/cookies/parsed_cookie.cc",
+        "net/cookies/site_for_cookies.cc",
+        "net/cookies/static_cookie_policy.cc",
+        "net/der/encode_values.cc",
+        "net/der/input.cc",
+        "net/der/parse_values.cc",
+        "net/der/parser.cc",
+        "net/der/tag.cc",
+        "net/disk_cache/backend_cleanup_tracker.cc",
+        "net/disk_cache/blockfile/addr.cc",
+        "net/disk_cache/blockfile/backend_impl.cc",
+        "net/disk_cache/blockfile/bitmap.cc",
+        "net/disk_cache/blockfile/block_files.cc",
+        "net/disk_cache/blockfile/disk_format.cc",
+        "net/disk_cache/blockfile/entry_impl.cc",
+        "net/disk_cache/blockfile/eviction.cc",
+        "net/disk_cache/blockfile/file.cc",
+        "net/disk_cache/blockfile/file_lock.cc",
+        "net/disk_cache/blockfile/file_posix.cc",
+        "net/disk_cache/blockfile/in_flight_backend_io.cc",
+        "net/disk_cache/blockfile/in_flight_io.cc",
+        "net/disk_cache/blockfile/mapped_file.cc",
+        "net/disk_cache/blockfile/rankings.cc",
+        "net/disk_cache/blockfile/sparse_control.cc",
+        "net/disk_cache/blockfile/stats.cc",
+        "net/disk_cache/cache_util.cc",
+        "net/disk_cache/cache_util_posix.cc",
+        "net/disk_cache/disk_cache.cc",
+        "net/disk_cache/memory/mem_backend_impl.cc",
+        "net/disk_cache/memory/mem_entry_impl.cc",
+        "net/disk_cache/net_log_parameters.cc",
+        "net/disk_cache/simple/post_doom_waiter.cc",
+        "net/disk_cache/simple/simple_backend_impl.cc",
+        "net/disk_cache/simple/simple_entry_format.cc",
+        "net/disk_cache/simple/simple_entry_impl.cc",
+        "net/disk_cache/simple/simple_entry_operation.cc",
+        "net/disk_cache/simple/simple_file_enumerator.cc",
+        "net/disk_cache/simple/simple_file_tracker.cc",
+        "net/disk_cache/simple/simple_index.cc",
+        "net/disk_cache/simple/simple_index_file.cc",
+        "net/disk_cache/simple/simple_net_log_parameters.cc",
+        "net/disk_cache/simple/simple_synchronous_entry.cc",
+        "net/disk_cache/simple/simple_util.cc",
+        "net/disk_cache/simple/simple_util_posix.cc",
+        "net/disk_cache/simple/simple_version_upgrade.cc",
+        "net/filter/brotli_source_stream.cc",
+        "net/filter/filter_source_stream.cc",
+        "net/filter/gzip_header.cc",
+        "net/filter/gzip_source_stream.cc",
+        "net/filter/source_stream.cc",
+        "net/first_party_sets/addition_overlaps_union_find.cc",
+        "net/first_party_sets/first_party_set_entry.cc",
+        "net/first_party_sets/first_party_set_metadata.cc",
+        "net/first_party_sets/first_party_sets_cache_filter.cc",
+        "net/first_party_sets/first_party_sets_context_config.cc",
+        "net/first_party_sets/global_first_party_sets.cc",
+        "net/first_party_sets/same_party_context.cc",
+        "net/http/alternative_service.cc",
+        "net/http/bidirectional_stream.cc",
+        "net/http/bidirectional_stream_impl.cc",
+        "net/http/bidirectional_stream_request_info.cc",
+        "net/http/broken_alternative_services.cc",
+        "net/http/http_auth.cc",
+        "net/http/http_auth_cache.cc",
+        "net/http/http_auth_challenge_tokenizer.cc",
+        "net/http/http_auth_controller.cc",
+        "net/http/http_auth_filter.cc",
+        "net/http/http_auth_handler.cc",
+        "net/http/http_auth_handler_basic.cc",
+        "net/http/http_auth_handler_digest.cc",
+        "net/http/http_auth_handler_factory.cc",
+        "net/http/http_auth_handler_negotiate.cc",
+        "net/http/http_auth_handler_ntlm.cc",
+        "net/http/http_auth_handler_ntlm_portable.cc",
+        "net/http/http_auth_multi_round_parse.cc",
+        "net/http/http_auth_ntlm_mechanism.cc",
+        "net/http/http_auth_preferences.cc",
+        "net/http/http_auth_scheme.cc",
+        "net/http/http_basic_state.cc",
+        "net/http/http_basic_stream.cc",
+        "net/http/http_byte_range.cc",
+        "net/http/http_cache.cc",
+        "net/http/http_cache_lookup_manager.cc",
+        "net/http/http_cache_transaction.cc",
+        "net/http/http_cache_writers.cc",
+        "net/http/http_chunked_decoder.cc",
+        "net/http/http_content_disposition.cc",
+        "net/http/http_log_util.cc",
+        "net/http/http_network_layer.cc",
+        "net/http/http_network_session.cc",
+        "net/http/http_network_session_peer.cc",
+        "net/http/http_network_transaction.cc",
+        "net/http/http_proxy_client_socket.cc",
+        "net/http/http_proxy_connect_job.cc",
+        "net/http/http_raw_request_headers.cc",
+        "net/http/http_request_headers.cc",
+        "net/http/http_request_info.cc",
+        "net/http/http_response_body_drainer.cc",
+        "net/http/http_response_headers.cc",
+        "net/http/http_response_info.cc",
+        "net/http/http_security_headers.cc",
+        "net/http/http_server_properties.cc",
+        "net/http/http_server_properties_manager.cc",
+        "net/http/http_status_code.cc",
+        "net/http/http_stream_factory.cc",
+        "net/http/http_stream_factory_job.cc",
+        "net/http/http_stream_factory_job_controller.cc",
+        "net/http/http_stream_parser.cc",
+        "net/http/http_stream_request.cc",
+        "net/http/http_util.cc",
+        "net/http/http_vary_data.cc",
+        "net/http/partial_data.cc",
+        "net/http/proxy_client_socket.cc",
+        "net/http/proxy_fallback.cc",
+        "net/http/transport_security_persister.cc",
+        "net/http/transport_security_state_source.cc",
+        "net/http/url_security_manager.cc",
+        "net/http/url_security_manager_posix.cc",
+        "net/http/webfonts_histogram.cc",
+        "net/log/file_net_log_observer.cc",
+        "net/log/net_log.cc",
+        "net/log/net_log_capture_mode.cc",
+        "net/log/net_log_entry.cc",
+        "net/log/net_log_event_type.cc",
+        "net/log/net_log_source.cc",
+        "net/log/net_log_util.cc",
+        "net/log/net_log_values.cc",
+        "net/log/net_log_with_source.cc",
+        "net/log/trace_net_log_observer.cc",
+        "net/network_error_logging/network_error_logging_service.cc",
+        "net/nqe/cached_network_quality.cc",
+        "net/nqe/effective_connection_type.cc",
+        "net/nqe/event_creator.cc",
+        "net/nqe/network_id.cc",
+        "net/nqe/network_qualities_prefs_manager.cc",
+        "net/nqe/network_quality.cc",
+        "net/nqe/network_quality_estimator.cc",
+        "net/nqe/network_quality_estimator_params.cc",
+        "net/nqe/network_quality_estimator_util.cc",
+        "net/nqe/network_quality_observation.cc",
+        "net/nqe/network_quality_store.cc",
+        "net/nqe/observation_buffer.cc",
+        "net/nqe/pref_names.cc",
+        "net/nqe/socket_watcher.cc",
+        "net/nqe/socket_watcher_factory.cc",
+        "net/nqe/throughput_analyzer.cc",
+        "net/ntlm/ntlm.cc",
+        "net/ntlm/ntlm_buffer_reader.cc",
+        "net/ntlm/ntlm_buffer_writer.cc",
+        "net/ntlm/ntlm_client.cc",
+        "net/ntlm/ntlm_constants.cc",
+        "net/proxy_resolution/configured_proxy_resolution_request.cc",
+        "net/proxy_resolution/configured_proxy_resolution_service.cc",
+        "net/proxy_resolution/dhcp_pac_file_fetcher.cc",
+        "net/proxy_resolution/multi_threaded_proxy_resolver.cc",
+        "net/proxy_resolution/network_delegate_error_observer.cc",
+        "net/proxy_resolution/pac_file_data.cc",
+        "net/proxy_resolution/pac_file_decider.cc",
+        "net/proxy_resolution/pac_file_fetcher.cc",
+        "net/proxy_resolution/pac_file_fetcher_impl.cc",
+        "net/proxy_resolution/polling_proxy_config_service.cc",
+        "net/proxy_resolution/proxy_bypass_rules.cc",
+        "net/proxy_resolution/proxy_config.cc",
+        "net/proxy_resolution/proxy_config_service.cc",
+        "net/proxy_resolution/proxy_config_service_android.cc",
+        "net/proxy_resolution/proxy_config_service_fixed.cc",
+        "net/proxy_resolution/proxy_config_with_annotation.cc",
+        "net/proxy_resolution/proxy_info.cc",
+        "net/proxy_resolution/proxy_list.cc",
+        "net/proxy_resolution/proxy_resolver_factory.cc",
+        "net/quic/bidirectional_stream_quic_impl.cc",
+        "net/quic/crypto/proof_source_chromium.cc",
+        "net/quic/crypto/proof_verifier_chromium.cc",
+        "net/quic/dedicated_web_transport_http3_client.cc",
+        "net/quic/network_connection.cc",
+        "net/quic/platform/impl/quic_chromium_clock.cc",
+        "net/quic/properties_based_quic_server_info.cc",
+        "net/quic/quic_address_mismatch.cc",
+        "net/quic/quic_chromium_alarm_factory.cc",
+        "net/quic/quic_chromium_client_session.cc",
+        "net/quic/quic_chromium_client_stream.cc",
+        "net/quic/quic_chromium_connection_helper.cc",
+        "net/quic/quic_chromium_packet_reader.cc",
+        "net/quic/quic_chromium_packet_writer.cc",
+        "net/quic/quic_clock_skew_detector.cc",
+        "net/quic/quic_connection_logger.cc",
+        "net/quic/quic_connectivity_monitor.cc",
+        "net/quic/quic_context.cc",
+        "net/quic/quic_crypto_client_config_handle.cc",
+        "net/quic/quic_crypto_client_stream_factory.cc",
+        "net/quic/quic_event_logger.cc",
+        "net/quic/quic_http3_logger.cc",
+        "net/quic/quic_http_stream.cc",
+        "net/quic/quic_http_utils.cc",
+        "net/quic/quic_proxy_client_socket.cc",
+        "net/quic/quic_server_info.cc",
+        "net/quic/quic_session_key.cc",
+        "net/quic/quic_stream_factory.cc",
+        "net/quic/set_quic_flag.cc",
+        "net/quic/web_transport_client.cc",
+        "net/quic/web_transport_error.cc",
+        "net/reporting/reporting_browsing_data_remover.cc",
+        "net/reporting/reporting_cache.cc",
+        "net/reporting/reporting_cache_impl.cc",
+        "net/reporting/reporting_cache_observer.cc",
+        "net/reporting/reporting_context.cc",
+        "net/reporting/reporting_delegate.cc",
+        "net/reporting/reporting_delivery_agent.cc",
+        "net/reporting/reporting_endpoint.cc",
+        "net/reporting/reporting_endpoint_manager.cc",
+        "net/reporting/reporting_garbage_collector.cc",
+        "net/reporting/reporting_header_parser.cc",
+        "net/reporting/reporting_network_change_observer.cc",
+        "net/reporting/reporting_policy.cc",
+        "net/reporting/reporting_report.cc",
+        "net/reporting/reporting_service.cc",
+        "net/reporting/reporting_uploader.cc",
+        "net/socket/client_socket_factory.cc",
+        "net/socket/client_socket_handle.cc",
+        "net/socket/client_socket_pool.cc",
+        "net/socket/client_socket_pool_manager.cc",
+        "net/socket/client_socket_pool_manager_impl.cc",
+        "net/socket/connect_job.cc",
+        "net/socket/connect_job_factory.cc",
+        "net/socket/network_binding_client_socket_factory.cc",
+        "net/socket/next_proto.cc",
+        "net/socket/server_socket.cc",
+        "net/socket/socket.cc",
+        "net/socket/socket_bio_adapter.cc",
+        "net/socket/socket_descriptor.cc",
+        "net/socket/socket_net_log_params.cc",
+        "net/socket/socket_options.cc",
+        "net/socket/socket_posix.cc",
+        "net/socket/socket_tag.cc",
+        "net/socket/socks5_client_socket.cc",
+        "net/socket/socks_client_socket.cc",
+        "net/socket/socks_connect_job.cc",
+        "net/socket/ssl_client_socket.cc",
+        "net/socket/ssl_client_socket_impl.cc",
+        "net/socket/ssl_connect_job.cc",
+        "net/socket/ssl_server_socket_impl.cc",
+        "net/socket/stream_socket.cc",
+        "net/socket/tcp_client_socket.cc",
+        "net/socket/tcp_server_socket.cc",
+        "net/socket/tcp_socket_posix.cc",
+        "net/socket/transport_client_socket.cc",
+        "net/socket/transport_client_socket_pool.cc",
+        "net/socket/transport_connect_job.cc",
+        "net/socket/transport_connect_sub_job.cc",
+        "net/socket/udp_client_socket.cc",
+        "net/socket/udp_net_log_parameters.cc",
+        "net/socket/udp_server_socket.cc",
+        "net/socket/udp_socket_global_limits.cc",
+        "net/socket/udp_socket_posix.cc",
+        "net/socket/unix_domain_client_socket_posix.cc",
+        "net/socket/unix_domain_server_socket_posix.cc",
+        "net/socket/websocket_endpoint_lock_manager.cc",
+        "net/socket/websocket_transport_client_socket_pool.cc",
+        "net/spdy/alps_decoder.cc",
+        "net/spdy/bidirectional_stream_spdy_impl.cc",
+        "net/spdy/buffered_spdy_framer.cc",
+        "net/spdy/header_coalescer.cc",
+        "net/spdy/http2_priority_dependencies.cc",
+        "net/spdy/http2_push_promise_index.cc",
+        "net/spdy/multiplexed_http_stream.cc",
+        "net/spdy/multiplexed_session.cc",
+        "net/spdy/spdy_buffer.cc",
+        "net/spdy/spdy_buffer_producer.cc",
+        "net/spdy/spdy_http_stream.cc",
+        "net/spdy/spdy_http_utils.cc",
+        "net/spdy/spdy_log_util.cc",
+        "net/spdy/spdy_proxy_client_socket.cc",
+        "net/spdy/spdy_read_queue.cc",
+        "net/spdy/spdy_session.cc",
+        "net/spdy/spdy_session_key.cc",
+        "net/spdy/spdy_session_pool.cc",
+        "net/spdy/spdy_stream.cc",
+        "net/spdy/spdy_write_queue.cc",
+        "net/ssl/cert_compression.cc",
+        "net/ssl/client_cert_identity.cc",
+        "net/ssl/openssl_ssl_util.cc",
+        "net/ssl/ssl_cert_request_info.cc",
+        "net/ssl/ssl_cipher_suite_names.cc",
+        "net/ssl/ssl_client_auth_cache.cc",
+        "net/ssl/ssl_client_session_cache.cc",
+        "net/ssl/ssl_config.cc",
+        "net/ssl/ssl_config_service.cc",
+        "net/ssl/ssl_config_service_defaults.cc",
+        "net/ssl/ssl_info.cc",
+        "net/ssl/ssl_key_logger.cc",
+        "net/ssl/ssl_key_logger_impl.cc",
+        "net/ssl/ssl_platform_key_android.cc",
+        "net/ssl/ssl_platform_key_util.cc",
+        "net/ssl/ssl_private_key.cc",
+        "net/ssl/ssl_server_config.cc",
+        "net/ssl/threaded_ssl_private_key.cc",
+        "net/url_request/redirect_info.cc",
+        "net/url_request/redirect_util.cc",
+        "net/url_request/report_sender.cc",
+        "net/url_request/static_http_user_agent_settings.cc",
+        "net/url_request/url_request.cc",
+        "net/url_request/url_request_context.cc",
+        "net/url_request/url_request_context_builder.cc",
+        "net/url_request/url_request_context_getter.cc",
+        "net/url_request/url_request_error_job.cc",
+        "net/url_request/url_request_filter.cc",
+        "net/url_request/url_request_http_job.cc",
+        "net/url_request/url_request_interceptor.cc",
+        "net/url_request/url_request_job.cc",
+        "net/url_request/url_request_job_factory.cc",
+        "net/url_request/url_request_netlog_params.cc",
+        "net/url_request/url_request_redirect_job.cc",
+        "net/url_request/url_request_throttler_entry.cc",
+        "net/url_request/url_request_throttler_manager.cc",
+        "net/url_request/view_cache_helper.cc",
+        "net/url_request/websocket_handshake_userdata_key.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    export_generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    export_static_lib_headers: [
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_third_party_quiche_quiche",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            srcs: [
+                "net/disk_cache/blockfile/mapped_file_bypass_mmap_posix.cc",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_branding_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_ios_cronet_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_branding_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+                "cronet_aml_net_ios_cronet_buildflags__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                "net/disk_cache/blockfile/mapped_file_bypass_mmap_posix.cc",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_branding_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_ios_cronet_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_branding_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+                "cronet_aml_net_ios_cronet_buildflags__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                "net/disk_cache/blockfile/mapped_file_posix.cc",
+            ],
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_branding_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_ios_cronet_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_branding_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+                "cronet_aml_net_ios_cronet_buildflags__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                "net/disk_cache/blockfile/mapped_file_bypass_mmap_posix.cc",
+            ],
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_branding_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_ios_cronet_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_branding_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+                "cronet_aml_net_ios_cronet_buildflags__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:net_deps
+cc_object {
+    name: "cronet_aml_net_net_deps",
+    srcs: [
+        ":cronet_aml_net_isolation_info_proto_gen",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_net_preload_decoder",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_brotli_common",
+        "cronet_aml_third_party_brotli_dec",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+    ],
+    generated_headers: [
+        "cronet_aml_net_isolation_info_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DENABLE_BUILT_IN_DNS",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DNET_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/brotli/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm",
+                "cronet_aml_net_net_jni_headers__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_arm64",
+                "cronet_aml_net_net_jni_headers__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86",
+                "cronet_aml_net_net_jni_headers__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_base_registry_controlled_domains_registry_controlled_domains__android_x86_64",
+                "cronet_aml_net_net_jni_headers__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:net_export_header
+cc_object {
+    name: "cronet_aml_net_net_export_header",
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //net:net_jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_net_net_jni_headers__android_arm",
+    srcs: [
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/net/net_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "AndroidCertVerifyResult_jni.h " +
+         "--output_name " +
+         "AndroidKeyStore_jni.h " +
+         "--output_name " +
+         "AndroidNetworkLibrary_jni.h " +
+         "--output_name " +
+         "AndroidTrafficStats_jni.h " +
+         "--output_name " +
+         "DnsStatus_jni.h " +
+         "--output_name " +
+         "GURLUtils_jni.h " +
+         "--output_name " +
+         "HttpNegotiateAuthenticator_jni.h " +
+         "--output_name " +
+         "HttpUtil_jni.h " +
+         "--output_name " +
+         "NetStringUtil_jni.h " +
+         "--output_name " +
+         "NetworkActiveNotifier_jni.h " +
+         "--output_name " +
+         "NetworkChangeNotifier_jni.h " +
+         "--output_name " +
+         "ProxyChangeListener_jni.h " +
+         "--output_name " +
+         "X509Util_jni.h " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidKeyStore.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidTrafficStats.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/DnsStatus.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/GURLUtils.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetStringUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkActiveNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkChangeNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/ProxyChangeListener.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/X509Util.java)",
+    out: [
+        "net/net_jni_headers/AndroidCertVerifyResult_jni.h",
+        "net/net_jni_headers/AndroidKeyStore_jni.h",
+        "net/net_jni_headers/AndroidNetworkLibrary_jni.h",
+        "net/net_jni_headers/AndroidTrafficStats_jni.h",
+        "net/net_jni_headers/DnsStatus_jni.h",
+        "net/net_jni_headers/GURLUtils_jni.h",
+        "net/net_jni_headers/HttpNegotiateAuthenticator_jni.h",
+        "net/net_jni_headers/HttpUtil_jni.h",
+        "net/net_jni_headers/NetStringUtil_jni.h",
+        "net/net_jni_headers/NetworkActiveNotifier_jni.h",
+        "net/net_jni_headers/NetworkChangeNotifier_jni.h",
+        "net/net_jni_headers/ProxyChangeListener_jni.h",
+        "net/net_jni_headers/X509Util_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_net_net_jni_headers__android_arm64",
+    srcs: [
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/net/net_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "AndroidCertVerifyResult_jni.h " +
+         "--output_name " +
+         "AndroidKeyStore_jni.h " +
+         "--output_name " +
+         "AndroidNetworkLibrary_jni.h " +
+         "--output_name " +
+         "AndroidTrafficStats_jni.h " +
+         "--output_name " +
+         "DnsStatus_jni.h " +
+         "--output_name " +
+         "GURLUtils_jni.h " +
+         "--output_name " +
+         "HttpNegotiateAuthenticator_jni.h " +
+         "--output_name " +
+         "HttpUtil_jni.h " +
+         "--output_name " +
+         "NetStringUtil_jni.h " +
+         "--output_name " +
+         "NetworkActiveNotifier_jni.h " +
+         "--output_name " +
+         "NetworkChangeNotifier_jni.h " +
+         "--output_name " +
+         "ProxyChangeListener_jni.h " +
+         "--output_name " +
+         "X509Util_jni.h " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidKeyStore.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidTrafficStats.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/DnsStatus.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/GURLUtils.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetStringUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkActiveNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkChangeNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/ProxyChangeListener.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/X509Util.java)",
+    out: [
+        "net/net_jni_headers/AndroidCertVerifyResult_jni.h",
+        "net/net_jni_headers/AndroidKeyStore_jni.h",
+        "net/net_jni_headers/AndroidNetworkLibrary_jni.h",
+        "net/net_jni_headers/AndroidTrafficStats_jni.h",
+        "net/net_jni_headers/DnsStatus_jni.h",
+        "net/net_jni_headers/GURLUtils_jni.h",
+        "net/net_jni_headers/HttpNegotiateAuthenticator_jni.h",
+        "net/net_jni_headers/HttpUtil_jni.h",
+        "net/net_jni_headers/NetStringUtil_jni.h",
+        "net/net_jni_headers/NetworkActiveNotifier_jni.h",
+        "net/net_jni_headers/NetworkChangeNotifier_jni.h",
+        "net/net_jni_headers/ProxyChangeListener_jni.h",
+        "net/net_jni_headers/X509Util_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_net_net_jni_headers__android_x86",
+    srcs: [
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/net/net_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "AndroidCertVerifyResult_jni.h " +
+         "--output_name " +
+         "AndroidKeyStore_jni.h " +
+         "--output_name " +
+         "AndroidNetworkLibrary_jni.h " +
+         "--output_name " +
+         "AndroidTrafficStats_jni.h " +
+         "--output_name " +
+         "DnsStatus_jni.h " +
+         "--output_name " +
+         "GURLUtils_jni.h " +
+         "--output_name " +
+         "HttpNegotiateAuthenticator_jni.h " +
+         "--output_name " +
+         "HttpUtil_jni.h " +
+         "--output_name " +
+         "NetStringUtil_jni.h " +
+         "--output_name " +
+         "NetworkActiveNotifier_jni.h " +
+         "--output_name " +
+         "NetworkChangeNotifier_jni.h " +
+         "--output_name " +
+         "ProxyChangeListener_jni.h " +
+         "--output_name " +
+         "X509Util_jni.h " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidKeyStore.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidTrafficStats.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/DnsStatus.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/GURLUtils.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetStringUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkActiveNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkChangeNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/ProxyChangeListener.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/X509Util.java)",
+    out: [
+        "net/net_jni_headers/AndroidCertVerifyResult_jni.h",
+        "net/net_jni_headers/AndroidKeyStore_jni.h",
+        "net/net_jni_headers/AndroidNetworkLibrary_jni.h",
+        "net/net_jni_headers/AndroidTrafficStats_jni.h",
+        "net/net_jni_headers/DnsStatus_jni.h",
+        "net/net_jni_headers/GURLUtils_jni.h",
+        "net/net_jni_headers/HttpNegotiateAuthenticator_jni.h",
+        "net/net_jni_headers/HttpUtil_jni.h",
+        "net/net_jni_headers/NetStringUtil_jni.h",
+        "net/net_jni_headers/NetworkActiveNotifier_jni.h",
+        "net/net_jni_headers/NetworkChangeNotifier_jni.h",
+        "net/net_jni_headers/ProxyChangeListener_jni.h",
+        "net/net_jni_headers/X509Util_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_net_net_jni_headers__android_x86_64",
+    srcs: [
+        "net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java",
+        "net/android/java/src/org/chromium/net/AndroidKeyStore.java",
+        "net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java",
+        "net/android/java/src/org/chromium/net/AndroidTrafficStats.java",
+        "net/android/java/src/org/chromium/net/DnsStatus.java",
+        "net/android/java/src/org/chromium/net/GURLUtils.java",
+        "net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java",
+        "net/android/java/src/org/chromium/net/HttpUtil.java",
+        "net/android/java/src/org/chromium/net/NetStringUtil.java",
+        "net/android/java/src/org/chromium/net/NetworkActiveNotifier.java",
+        "net/android/java/src/org/chromium/net/NetworkChangeNotifier.java",
+        "net/android/java/src/org/chromium/net/ProxyChangeListener.java",
+        "net/android/java/src/org/chromium/net/X509Util.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/net/net_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "AndroidCertVerifyResult_jni.h " +
+         "--output_name " +
+         "AndroidKeyStore_jni.h " +
+         "--output_name " +
+         "AndroidNetworkLibrary_jni.h " +
+         "--output_name " +
+         "AndroidTrafficStats_jni.h " +
+         "--output_name " +
+         "DnsStatus_jni.h " +
+         "--output_name " +
+         "GURLUtils_jni.h " +
+         "--output_name " +
+         "HttpNegotiateAuthenticator_jni.h " +
+         "--output_name " +
+         "HttpUtil_jni.h " +
+         "--output_name " +
+         "NetStringUtil_jni.h " +
+         "--output_name " +
+         "NetworkActiveNotifier_jni.h " +
+         "--output_name " +
+         "NetworkChangeNotifier_jni.h " +
+         "--output_name " +
+         "ProxyChangeListener_jni.h " +
+         "--output_name " +
+         "X509Util_jni.h " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidCertVerifyResult.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidKeyStore.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/AndroidTrafficStats.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/DnsStatus.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/GURLUtils.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/HttpUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetStringUtil.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkActiveNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/NetworkChangeNotifier.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/ProxyChangeListener.java) " +
+         "--input_file " +
+         "$(location net/android/java/src/org/chromium/net/X509Util.java)",
+    out: [
+        "net/net_jni_headers/AndroidCertVerifyResult_jni.h",
+        "net/net_jni_headers/AndroidKeyStore_jni.h",
+        "net/net_jni_headers/AndroidNetworkLibrary_jni.h",
+        "net/net_jni_headers/AndroidTrafficStats_jni.h",
+        "net/net_jni_headers/DnsStatus_jni.h",
+        "net/net_jni_headers/GURLUtils_jni.h",
+        "net/net_jni_headers/HttpNegotiateAuthenticator_jni.h",
+        "net/net_jni_headers/HttpUtil_jni.h",
+        "net/net_jni_headers/NetStringUtil_jni.h",
+        "net/net_jni_headers/NetworkActiveNotifier_jni.h",
+        "net/net_jni_headers/NetworkChangeNotifier_jni.h",
+        "net/net_jni_headers/ProxyChangeListener_jni.h",
+        "net/net_jni_headers/X509Util_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_nqe_proto
+cc_genrule {
+    name: "cronet_aml_net_net_nqe_proto_gen",
+    srcs: [
+        "net/nqe/proto/network_id_proto.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/nqe/proto --cpp_out=lite=true:$(genDir)/external/chromium_org/net/nqe/proto/ $(in)",
+    out: [
+        "external/chromium_org/net/nqe/proto/network_id_proto.pb.cc",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_nqe_proto
+cc_genrule {
+    name: "cronet_aml_net_net_nqe_proto_gen_headers",
+    srcs: [
+        "net/nqe/proto/network_id_proto.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/nqe/proto --cpp_out=lite=true:$(genDir)/external/chromium_org/net/nqe/proto/ $(in)",
+    out: [
+        "external/chromium_org/net/nqe/proto/network_id_proto.pb.h",
+    ],
+    export_include_dirs: [
+        ".",
+        "net/nqe/proto",
+        "protos",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net:net_public_deps
+cc_object {
+    name: "cronet_aml_net_net_public_deps",
+    srcs: [
+        ":cronet_aml_net_net_nqe_proto_gen",
+        ":cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_crypto_crypto",
+        "cronet_aml_net_third_party_quiche_quiche",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_net_nqe_proto_gen_headers",
+        "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_net_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_net_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_net_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_net_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:preload_decoder
+cc_library_static {
+    name: "cronet_aml_net_preload_decoder",
+    srcs: [
+        "net/extras/preload_data/decoder.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //net/third_party/quiche:net_quic_proto
+cc_genrule {
+    name: "cronet_aml_net_third_party_quiche_net_quic_proto_gen",
+    srcs: [
+        "net/third_party/quiche/src/quiche/quic/core/proto/cached_network_parameters.proto",
+        "net/third_party/quiche/src/quiche/quic/core/proto/crypto_server_config.proto",
+        "net/third_party/quiche/src/quiche/quic/core/proto/source_address_token.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/third_party/quiche/src --cpp_out=lite=true:$(genDir)/external/chromium_org/net/third_party/quiche/src/ $(in)",
+    out: [
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/cached_network_parameters.pb.cc",
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/crypto_server_config.pb.cc",
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/source_address_token.pb.cc",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/third_party/quiche:net_quic_proto
+cc_genrule {
+    name: "cronet_aml_net_third_party_quiche_net_quic_proto_gen_headers",
+    srcs: [
+        "net/third_party/quiche/src/quiche/quic/core/proto/cached_network_parameters.proto",
+        "net/third_party/quiche/src/quiche/quic/core/proto/crypto_server_config.proto",
+        "net/third_party/quiche/src/quiche/quic/core/proto/source_address_token.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/third_party/quiche/src --cpp_out=lite=true:$(genDir)/external/chromium_org/net/third_party/quiche/src/ $(in)",
+    out: [
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/cached_network_parameters.pb.h",
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/crypto_server_config.pb.h",
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/core/proto/source_address_token.pb.h",
+    ],
+    export_include_dirs: [
+        ".",
+        "net/third_party/quiche/src",
+        "protos",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/third_party/quiche:net_quic_test_tools_proto
+cc_genrule {
+    name: "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen",
+    srcs: [
+        "net/third_party/quiche/src/quiche/quic/test_tools/send_algorithm_test_result.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools --cpp_out=lite=true:$(genDir)/external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools/ $(in)",
+    out: [
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools/send_algorithm_test_result.pb.cc",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/third_party/quiche:net_quic_test_tools_proto
+cc_genrule {
+    name: "cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers",
+    srcs: [
+        "net/third_party/quiche/src/quiche/quic/test_tools/send_algorithm_test_result.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools --cpp_out=lite=true:$(genDir)/external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools/ $(in)",
+    out: [
+        "external/chromium_org/net/third_party/quiche/src/quiche/quic/test_tools/send_algorithm_test_result.pb.h",
+    ],
+    export_include_dirs: [
+        ".",
+        "net/third_party/quiche/src/quiche/quic/test_tools",
+        "protos",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //net/third_party/quiche:quiche
+cc_library_static {
+    name: "cronet_aml_net_third_party_quiche_quiche",
+    srcs: [
+        ":cronet_aml_net_third_party_quiche_net_quic_proto_gen",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_base",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_log_severity",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_malloc_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_raw_logging_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_spinlock_wait",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_strerror",
+        ":cronet_aml_third_party_abseil_cpp_absl_base_throw_delegate",
+        ":cronet_aml_third_party_abseil_cpp_absl_container_hashtablez_sampler",
+        ":cronet_aml_third_party_abseil_cpp_absl_container_raw_hash_set",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_debugging_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_demangle_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_examine_stack",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_failure_signal_handler",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_stacktrace",
+        ":cronet_aml_third_party_abseil_cpp_absl_debugging_symbolize",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_city",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_hash",
+        ":cronet_aml_third_party_abseil_cpp_absl_hash_low_level_hash",
+        ":cronet_aml_third_party_abseil_cpp_absl_numeric_int128",
+        ":cronet_aml_third_party_abseil_cpp_absl_profiling_exponential_biased",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_distributions",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_platform",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_pool_urbg",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes_impl",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_slow",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_internal_seed_material",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_seed_gen_exception",
+        ":cronet_aml_third_party_abseil_cpp_absl_random_seed_sequences",
+        ":cronet_aml_third_party_abseil_cpp_absl_status_status",
+        ":cronet_aml_third_party_abseil_cpp_absl_status_statusor",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cord",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cord_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_functions",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_handle",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_cordz_info",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_str_format_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_strings_strings",
+        ":cronet_aml_third_party_abseil_cpp_absl_synchronization_graphcycles_internal",
+        ":cronet_aml_third_party_abseil_cpp_absl_synchronization_synchronization",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_civil_time",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_time_zone",
+        ":cronet_aml_third_party_abseil_cpp_absl_time_time",
+        ":cronet_aml_third_party_abseil_cpp_absl_types_bad_optional_access",
+        ":cronet_aml_third_party_abseil_cpp_absl_types_bad_variant_access",
+        "net/third_party/quiche/overrides/quiche_platform_impl/quiche_mutex_impl.cc",
+        "net/third_party/quiche/overrides/quiche_platform_impl/quiche_time_utils_impl.cc",
+        "net/third_party/quiche/overrides/quiche_platform_impl/quiche_url_utils_impl.cc",
+        "net/third_party/quiche/src/quiche/common/platform/api/quiche_hostname_utils.cc",
+        "net/third_party/quiche/src/quiche/common/platform/api/quiche_mutex.cc",
+        "net/third_party/quiche/src/quiche/common/platform/default/quiche_platform_impl/quiche_flags_impl.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_buffer_allocator.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_crypto_logging.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_data_reader.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_data_writer.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_ip_address.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_ip_address_family.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_mem_slice_storage.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_random.cc",
+        "net/third_party/quiche/src/quiche/common/quiche_text_utils.cc",
+        "net/third_party/quiche/src/quiche/common/simple_buffer_allocator.cc",
+        "net/third_party/quiche/src/quiche/common/structured_headers.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/event_forwarder.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/header_validator.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/http2_protocol.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/http2_util.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/noop_header_validator.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/oghttp2_adapter.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/oghttp2_session.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/oghttp2_util.cc",
+        "net/third_party/quiche/src/quiche/http2/adapter/window_manager.cc",
+        "net/third_party/quiche/src/quiche/http2/core/http2_trace_logging.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/decode_buffer.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/decode_http2_structures.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/decode_status.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/frame_decoder_state.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/http2_frame_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/http2_frame_decoder_listener.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/http2_structure_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/altsvc_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/continuation_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/data_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/goaway_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/headers_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/ping_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/priority_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/priority_update_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/push_promise_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/settings_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/unknown_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/window_update_payload_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc",
+        "net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc",
+        "net/third_party/quiche/src/quiche/http2/http2_constants.cc",
+        "net/third_party/quiche/src/quiche/http2/http2_structures.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_drain.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_misc.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_probe_bw.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_probe_rtt.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr2_startup.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/bbr_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/cubic_bytes.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/general_loss_algorithm.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/hybrid_slow_start.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/pacing_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/prr_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/rtt_stats.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/send_algorithm_interface.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/tcp_cubic_sender_bytes.cc",
+        "net/third_party/quiche/src/quiche/quic/core/congestion_control/uber_loss_algorithm.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aead_base_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_256_gcm_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/aes_base_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/cert_compressor.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/certificate_util.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/certificate_view.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/chacha_base_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/channel_id.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/client_proof_source.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_framer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake_message.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_secret_boxer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_utils.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/curve25519_key_exchange.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/key_exchange.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/null_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/null_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/p256_key_exchange.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/proof_source.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/proof_source_x509.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_compressed_certs_cache.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_client_config.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_proof.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_crypto_server_config.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/quic_hkdf.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/tls_client_connection.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/tls_connection.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/tls_server_connection.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/transport_parameters.cc",
+        "net/third_party/quiche/src/quiche/quic/core/crypto/web_transport_fingerprint_proof_verifier.cc",
+        "net/third_party/quiche/src/quiche/quic/core/deterministic_connection_id_generator.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_ack_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_ack_frequency_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_blocked_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_connection_close_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_crypto_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_goaway_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_handshake_done_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_max_streams_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_message_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_new_connection_id_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_new_token_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_padding_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_path_challenge_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_path_response_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_ping_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_retire_connection_id_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_rst_stream_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_stop_sending_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_stop_waiting_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_stream_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_streams_blocked_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/frames/quic_window_update_frame.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/capsule.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/http_constants.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/http_decoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/http_encoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_client_promised_info.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_client_push_promise_index.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_header_list.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_headers_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_receive_control_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_send_control_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_server_initiated_spdy_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_server_session_base.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_client_session.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_client_session_base.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_client_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_session.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/quic_spdy_stream_body_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/spdy_server_push_utils.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/web_transport_http3.cc",
+        "net/third_party/quiche/src/quiche/quic/core/http/web_transport_stream_adapter.cc",
+        "net/third_party/quiche/src/quiche/quic/core/legacy_quic_stream_id_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_blocking_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_decoded_headers_accumulator.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_decoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_decoder_stream_receiver.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_decoder_stream_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_encoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_encoder_stream_receiver.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_encoder_stream_sender.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_header_table.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_index_conversions.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_instruction_decoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_instruction_encoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_instructions.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_progressive_decoder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_receive_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_required_insert_count.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_send_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/qpack_static_table.cc",
+        "net/third_party/quiche/src/quiche/quic/core/qpack/value_splitting_header_list.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_ack_listener_interface.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_alarm.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_bandwidth.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_chaos_protector.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_clock.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_coalesced_packet.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_config.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_connection.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_connection_context.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_connection_id.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_connection_id_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_connection_stats.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_constants.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_control_frame_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_client_handshaker.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_client_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_handshaker.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_server_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_server_stream_base.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_data_reader.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_data_writer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_datagram_queue.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_error_codes.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_flow_controller.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_framer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_idle_network_detector.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_mtu_discovery.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_network_blackhole_detector.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_packet_creator.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_packet_number.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_packets.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_path_validator.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_ping_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_received_packet_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_sent_packet_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_server_id.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_session.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_socket_address_coder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_stream.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_stream_id_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_stream_send_buffer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_stream_sequencer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_stream_sequencer_buffer.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_sustained_bandwidth_recorder.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_tag.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_time.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_transmission_info.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_types.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_unacked_packet_map.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_utils.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_version_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_versions.cc",
+        "net/third_party/quiche/src/quiche/quic/core/quic_write_blocked_list.cc",
+        "net/third_party/quiche/src/quiche/quic/core/tls_client_handshaker.cc",
+        "net/third_party/quiche/src/quiche/quic/core/tls_handshaker.cc",
+        "net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc",
+        "net/third_party/quiche/src/quiche/quic/core/uber_quic_stream_id_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/core/uber_received_packet_manager.cc",
+        "net/third_party/quiche/src/quiche/quic/platform/api/quic_socket_address.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/array_output_buffer.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_constants.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_decoder_adapter.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_encoder.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_entry.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_header_table.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_output_stream.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_static_table.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/http2_frame_decoder_adapter.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/http2_header_storage.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/recording_headers_handler.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_alt_svc_wire_format.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_frame_builder.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_framer.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_no_op_visitor.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_pinnable_buffer_piece.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_prefixed_buffer_reader.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.cc",
+        "net/third_party/quiche/src/quiche/spdy/core/spdy_simple_arena.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_net_uri_template",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+        "cronet_aml_third_party_protobuf_protobuf_lite",
+        "cronet_aml_third_party_zlib_zlib",
+        "cronet_aml_url_url",
+    ],
+    generated_headers: [
+        "cronet_aml_net_third_party_quiche_net_quic_proto_gen_headers",
+    ],
+    export_generated_headers: [
+        "cronet_aml_net_third_party_quiche_net_quic_proto_gen_headers",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-DIS_QUICHE_IMPL",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "net/third_party/quiche/overrides/",
+        "net/third_party/quiche/src/",
+        "net/third_party/quiche/src/quiche/common/platform/default/",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net/traffic_annotation:traffic_annotation
+cc_object {
+    name: "cronet_aml_net_traffic_annotation_traffic_annotation",
+    srcs: [
+        "net/traffic_annotation/network_traffic_annotation_android.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //net:uri_template
+cc_library_static {
+    name: "cronet_aml_net_uri_template",
+    srcs: [
+        "net/third_party/uri_template/uri_template.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-DIS_URI_TEMPLATE_IMPL",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp:absl
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/algorithm:algorithm
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_algorithm_algorithm",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/algorithm:container
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_algorithm_container",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:atomic_hook
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_atomic_hook",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:base
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_base",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/cycleclock.cc",
+        "third_party/abseil-cpp/absl/base/internal/spinlock.cc",
+        "third_party/abseil-cpp/absl/base/internal/sysinfo.cc",
+        "third_party/abseil-cpp/absl/base/internal/thread_identity.cc",
+        "third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:base_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_base_internal",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:config
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_config",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:core_headers
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_core_headers",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:cycleclock_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_cycleclock_internal",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:dynamic_annotations
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_dynamic_annotations",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:endian
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_endian",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:errno_saver
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_errno_saver",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:fast_type_id
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_fast_type_id",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:log_severity
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_log_severity",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/log_severity.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:malloc_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_malloc_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:prefetch
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_prefetch",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:raw_logging_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_raw_logging_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/raw_logging.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:spinlock_wait
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_spinlock_wait",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/spinlock_wait.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:strerror
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_strerror",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/strerror.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/base:throw_delegate
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_base_throw_delegate",
+    srcs: [
+        "third_party/abseil-cpp/absl/base/internal/throw_delegate.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/cleanup:cleanup
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_cleanup_cleanup",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/cleanup:cleanup_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_cleanup_cleanup_internal",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:btree
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_btree",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:common
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_common",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:common_policy_traits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_common_policy_traits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:compressed_tuple
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_compressed_tuple",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:container_memory
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_container_memory",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:fixed_array
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_fixed_array",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:flat_hash_map
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_flat_hash_map",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:flat_hash_set
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_flat_hash_set",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:hash_function_defaults
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_hash_function_defaults",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:hash_policy_traits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_hash_policy_traits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:hashtable_debug_hooks
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_hashtable_debug_hooks",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:hashtablez_sampler
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_hashtablez_sampler",
+    srcs: [
+        "third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc",
+        "third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:inlined_vector
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_inlined_vector",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:inlined_vector_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_inlined_vector_internal",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:layout
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_layout",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:node_hash_map
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_node_hash_map",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:node_hash_set
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_node_hash_set",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:node_slot_policy
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_node_slot_policy",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:raw_hash_map
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_raw_hash_map",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/container:raw_hash_set
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_container_raw_hash_set",
+    srcs: [
+        "third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:debugging_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_debugging_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc",
+        "third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc",
+        "third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:demangle_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_demangle_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/internal/demangle.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:examine_stack
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_examine_stack",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:failure_signal_handler
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_failure_signal_handler",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:stacktrace
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_stacktrace",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/stacktrace.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/debugging:symbolize
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_debugging_symbolize",
+    srcs: [
+        "third_party/abseil-cpp/absl/debugging/symbolize.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/functional:any_invocable
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_functional_any_invocable",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/functional:bind_front
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_functional_bind_front",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/functional:function_ref
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_functional_function_ref",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/hash:city
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_hash_city",
+    srcs: [
+        "third_party/abseil-cpp/absl/hash/internal/city.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/hash:hash
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_hash_hash",
+    srcs: [
+        "third_party/abseil-cpp/absl/hash/internal/hash.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/hash:low_level_hash
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_hash_low_level_hash",
+    srcs: [
+        "third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/memory:memory
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_memory_memory",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/meta:type_traits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_meta_type_traits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/numeric:bits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_numeric_bits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/numeric:int128
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_numeric_int128",
+    srcs: [
+        "third_party/abseil-cpp/absl/numeric/int128.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/numeric:representation
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_numeric_representation",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/profiling:exponential_biased
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_profiling_exponential_biased",
+    srcs: [
+        "third_party/abseil-cpp/absl/profiling/internal/exponential_biased.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/profiling:sample_recorder
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_profiling_sample_recorder",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random:distributions
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_distributions",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/discrete_distribution.cc",
+        "third_party/abseil-cpp/absl/random/gaussian_distribution.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:distribution_caller
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_distribution_caller",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:fast_uniform_bits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_fast_uniform_bits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:fastmath
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_fastmath",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:generate_real
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_generate_real",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:iostream_state_saver
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_iostream_state_saver",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:nonsecure_base
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_nonsecure_base",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:pcg_engine
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_pcg_engine",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:platform
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_platform",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:pool_urbg
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_pool_urbg",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/pool_urbg.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:randen
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_randen",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/randen.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:randen_engine
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_engine",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:randen_hwaes
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/randen_detect.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:randen_hwaes_impl
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_hwaes_impl",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:randen_slow
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_randen_slow",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/randen_slow.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:salted_seed_seq
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_salted_seed_seq",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:seed_material
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_seed_material",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/internal/seed_material.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:traits
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_traits",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:uniform_helper
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_uniform_helper",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random/internal:wide_multiply
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_internal_wide_multiply",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random:random
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_random",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random:seed_gen_exception
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_seed_gen_exception",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/seed_gen_exception.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/random:seed_sequences
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_random_seed_sequences",
+    srcs: [
+        "third_party/abseil-cpp/absl/random/seed_sequences.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_build_chromeos_buildflags__host",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/status:status
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_status_status",
+    srcs: [
+        "third_party/abseil-cpp/absl/status/status.cc",
+        "third_party/abseil-cpp/absl/status/status_payload_printer.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/status:statusor
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_status_statusor",
+    srcs: [
+        "third_party/abseil-cpp/absl/status/statusor.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cord
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cord",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/cord.cc",
+        "third_party/abseil-cpp/absl/strings/cord_analysis.cc",
+        "third_party/abseil-cpp/absl/strings/cord_buffer.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cord_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cord_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/cord_internal.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_crc.cc",
+        "third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_functions
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_functions",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/cordz_functions.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_handle
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_handle",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_info
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_info",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/cordz_info.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_statistics
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_statistics",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_update_scope
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_update_scope",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:cordz_update_tracker
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_cordz_update_tracker",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/escaping.cc",
+        "third_party/abseil-cpp/absl/strings/internal/ostringstream.cc",
+        "third_party/abseil-cpp/absl/strings/internal/utf8.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:str_format
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_str_format",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:str_format_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_str_format_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc",
+        "third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc",
+        "third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc",
+        "third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc",
+        "third_party/abseil-cpp/absl/strings/internal/str_format/output.cc",
+        "third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/strings:strings
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_strings_strings",
+    srcs: [
+        "third_party/abseil-cpp/absl/strings/ascii.cc",
+        "third_party/abseil-cpp/absl/strings/charconv.cc",
+        "third_party/abseil-cpp/absl/strings/escaping.cc",
+        "third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc",
+        "third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc",
+        "third_party/abseil-cpp/absl/strings/internal/damerau_levenshtein_distance.cc",
+        "third_party/abseil-cpp/absl/strings/internal/memutil.cc",
+        "third_party/abseil-cpp/absl/strings/match.cc",
+        "third_party/abseil-cpp/absl/strings/numbers.cc",
+        "third_party/abseil-cpp/absl/strings/str_cat.cc",
+        "third_party/abseil-cpp/absl/strings/str_replace.cc",
+        "third_party/abseil-cpp/absl/strings/str_split.cc",
+        "third_party/abseil-cpp/absl/strings/string_view.cc",
+        "third_party/abseil-cpp/absl/strings/substitute.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/synchronization:graphcycles_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_synchronization_graphcycles_internal",
+    srcs: [
+        "third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/synchronization:kernel_timeout_internal
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_synchronization_kernel_timeout_internal",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/synchronization:synchronization
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_synchronization_synchronization",
+    srcs: [
+        "third_party/abseil-cpp/absl/synchronization/barrier.cc",
+        "third_party/abseil-cpp/absl/synchronization/blocking_counter.cc",
+        "third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc",
+        "third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc",
+        "third_party/abseil-cpp/absl/synchronization/internal/waiter.cc",
+        "third_party/abseil-cpp/absl/synchronization/mutex.cc",
+        "third_party/abseil-cpp/absl/synchronization/notification.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/time/internal/cctz:civil_time
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_civil_time",
+    srcs: [
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_detail.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/time/internal/cctz:time_zone
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_time_internal_cctz_time_zone",
+    srcs: [
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_posix.cc",
+        "third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/time:time
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_time_time",
+    srcs: [
+        "third_party/abseil-cpp/absl/time/civil_time.cc",
+        "third_party/abseil-cpp/absl/time/clock.cc",
+        "third_party/abseil-cpp/absl/time/duration.cc",
+        "third_party/abseil-cpp/absl/time/format.cc",
+        "third_party/abseil-cpp/absl/time/time.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:bad_optional_access
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_bad_optional_access",
+    srcs: [
+        "third_party/abseil-cpp/absl/types/bad_optional_access.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:bad_variant_access
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_bad_variant_access",
+    srcs: [
+        "third_party/abseil-cpp/absl/types/bad_variant_access.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:compare
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_compare",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:optional
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_optional",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:span
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_span",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/types:variant
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_types_variant",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/abseil-cpp/absl/utility:utility
+cc_object {
+    name: "cronet_aml_third_party_abseil_cpp_absl_utility_utility",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DABSL_ALLOCATOR_NOTHROW=1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/android_ndk:cpu_features
+cc_object {
+    name: "cronet_aml_third_party_android_ndk_cpu_features",
+    srcs: [
+        "third_party/android_ndk/sources/android/cpufeatures/cpu-features.c",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/android_ndk/sources/android/cpufeatures/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/ashmem:ashmem
+cc_object {
+    name: "cronet_aml_third_party_ashmem_ashmem",
+    srcs: [
+        "third_party/ashmem/ashmem-dev.c",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/boringssl:boringssl
+cc_library_static {
+    name: "cronet_aml_third_party_boringssl_boringssl",
+    srcs: [
+        ":cronet_aml_third_party_boringssl_boringssl_asm",
+        "third_party/boringssl/err_data.c",
+        "third_party/boringssl/src/crypto/asn1/a_bitstr.c",
+        "third_party/boringssl/src/crypto/asn1/a_bool.c",
+        "third_party/boringssl/src/crypto/asn1/a_d2i_fp.c",
+        "third_party/boringssl/src/crypto/asn1/a_dup.c",
+        "third_party/boringssl/src/crypto/asn1/a_gentm.c",
+        "third_party/boringssl/src/crypto/asn1/a_i2d_fp.c",
+        "third_party/boringssl/src/crypto/asn1/a_int.c",
+        "third_party/boringssl/src/crypto/asn1/a_mbstr.c",
+        "third_party/boringssl/src/crypto/asn1/a_object.c",
+        "third_party/boringssl/src/crypto/asn1/a_octet.c",
+        "third_party/boringssl/src/crypto/asn1/a_print.c",
+        "third_party/boringssl/src/crypto/asn1/a_strex.c",
+        "third_party/boringssl/src/crypto/asn1/a_strnid.c",
+        "third_party/boringssl/src/crypto/asn1/a_time.c",
+        "third_party/boringssl/src/crypto/asn1/a_type.c",
+        "third_party/boringssl/src/crypto/asn1/a_utctm.c",
+        "third_party/boringssl/src/crypto/asn1/a_utf8.c",
+        "third_party/boringssl/src/crypto/asn1/asn1_lib.c",
+        "third_party/boringssl/src/crypto/asn1/asn1_par.c",
+        "third_party/boringssl/src/crypto/asn1/asn_pack.c",
+        "third_party/boringssl/src/crypto/asn1/f_int.c",
+        "third_party/boringssl/src/crypto/asn1/f_string.c",
+        "third_party/boringssl/src/crypto/asn1/posix_time.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_dec.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_enc.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_fre.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_new.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_typ.c",
+        "third_party/boringssl/src/crypto/asn1/tasn_utl.c",
+        "third_party/boringssl/src/crypto/base64/base64.c",
+        "third_party/boringssl/src/crypto/bio/bio.c",
+        "third_party/boringssl/src/crypto/bio/bio_mem.c",
+        "third_party/boringssl/src/crypto/bio/connect.c",
+        "third_party/boringssl/src/crypto/bio/fd.c",
+        "third_party/boringssl/src/crypto/bio/file.c",
+        "third_party/boringssl/src/crypto/bio/hexdump.c",
+        "third_party/boringssl/src/crypto/bio/pair.c",
+        "third_party/boringssl/src/crypto/bio/printf.c",
+        "third_party/boringssl/src/crypto/bio/socket.c",
+        "third_party/boringssl/src/crypto/bio/socket_helper.c",
+        "third_party/boringssl/src/crypto/blake2/blake2.c",
+        "third_party/boringssl/src/crypto/bn_extra/bn_asn1.c",
+        "third_party/boringssl/src/crypto/bn_extra/convert.c",
+        "third_party/boringssl/src/crypto/buf/buf.c",
+        "third_party/boringssl/src/crypto/bytestring/asn1_compat.c",
+        "third_party/boringssl/src/crypto/bytestring/ber.c",
+        "third_party/boringssl/src/crypto/bytestring/cbb.c",
+        "third_party/boringssl/src/crypto/bytestring/cbs.c",
+        "third_party/boringssl/src/crypto/bytestring/unicode.c",
+        "third_party/boringssl/src/crypto/chacha/chacha.c",
+        "third_party/boringssl/src/crypto/cipher_extra/cipher_extra.c",
+        "third_party/boringssl/src/crypto/cipher_extra/derive_key.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_aesctrhmac.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_aesgcmsiv.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_chacha20poly1305.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_des.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_null.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_rc2.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_rc4.c",
+        "third_party/boringssl/src/crypto/cipher_extra/e_tls.c",
+        "third_party/boringssl/src/crypto/cipher_extra/tls_cbc.c",
+        "third_party/boringssl/src/crypto/conf/conf.c",
+        "third_party/boringssl/src/crypto/cpu_aarch64_apple.c",
+        "third_party/boringssl/src/crypto/cpu_aarch64_fuchsia.c",
+        "third_party/boringssl/src/crypto/cpu_aarch64_linux.c",
+        "third_party/boringssl/src/crypto/cpu_aarch64_win.c",
+        "third_party/boringssl/src/crypto/cpu_arm.c",
+        "third_party/boringssl/src/crypto/cpu_arm_linux.c",
+        "third_party/boringssl/src/crypto/cpu_intel.c",
+        "third_party/boringssl/src/crypto/cpu_ppc64le.c",
+        "third_party/boringssl/src/crypto/crypto.c",
+        "third_party/boringssl/src/crypto/curve25519/curve25519.c",
+        "third_party/boringssl/src/crypto/curve25519/spake25519.c",
+        "third_party/boringssl/src/crypto/des/des.c",
+        "third_party/boringssl/src/crypto/dh_extra/dh_asn1.c",
+        "third_party/boringssl/src/crypto/dh_extra/params.c",
+        "third_party/boringssl/src/crypto/digest_extra/digest_extra.c",
+        "third_party/boringssl/src/crypto/dsa/dsa.c",
+        "third_party/boringssl/src/crypto/dsa/dsa_asn1.c",
+        "third_party/boringssl/src/crypto/ec_extra/ec_asn1.c",
+        "third_party/boringssl/src/crypto/ec_extra/ec_derive.c",
+        "third_party/boringssl/src/crypto/ec_extra/hash_to_curve.c",
+        "third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c",
+        "third_party/boringssl/src/crypto/ecdsa_extra/ecdsa_asn1.c",
+        "third_party/boringssl/src/crypto/engine/engine.c",
+        "third_party/boringssl/src/crypto/err/err.c",
+        "third_party/boringssl/src/crypto/evp/evp.c",
+        "third_party/boringssl/src/crypto/evp/evp_asn1.c",
+        "third_party/boringssl/src/crypto/evp/evp_ctx.c",
+        "third_party/boringssl/src/crypto/evp/p_dsa_asn1.c",
+        "third_party/boringssl/src/crypto/evp/p_ec.c",
+        "third_party/boringssl/src/crypto/evp/p_ec_asn1.c",
+        "third_party/boringssl/src/crypto/evp/p_ed25519.c",
+        "third_party/boringssl/src/crypto/evp/p_ed25519_asn1.c",
+        "third_party/boringssl/src/crypto/evp/p_hkdf.c",
+        "third_party/boringssl/src/crypto/evp/p_rsa.c",
+        "third_party/boringssl/src/crypto/evp/p_rsa_asn1.c",
+        "third_party/boringssl/src/crypto/evp/p_x25519.c",
+        "third_party/boringssl/src/crypto/evp/p_x25519_asn1.c",
+        "third_party/boringssl/src/crypto/evp/pbkdf.c",
+        "third_party/boringssl/src/crypto/evp/print.c",
+        "third_party/boringssl/src/crypto/evp/scrypt.c",
+        "third_party/boringssl/src/crypto/evp/sign.c",
+        "third_party/boringssl/src/crypto/ex_data.c",
+        "third_party/boringssl/src/crypto/fipsmodule/bcm.c",
+        "third_party/boringssl/src/crypto/fipsmodule/fips_shared_support.c",
+        "third_party/boringssl/src/crypto/hkdf/hkdf.c",
+        "third_party/boringssl/src/crypto/hpke/hpke.c",
+        "third_party/boringssl/src/crypto/hrss/hrss.c",
+        "third_party/boringssl/src/crypto/lhash/lhash.c",
+        "third_party/boringssl/src/crypto/mem.c",
+        "third_party/boringssl/src/crypto/obj/obj.c",
+        "third_party/boringssl/src/crypto/obj/obj_xref.c",
+        "third_party/boringssl/src/crypto/pem/pem_all.c",
+        "third_party/boringssl/src/crypto/pem/pem_info.c",
+        "third_party/boringssl/src/crypto/pem/pem_lib.c",
+        "third_party/boringssl/src/crypto/pem/pem_oth.c",
+        "third_party/boringssl/src/crypto/pem/pem_pk8.c",
+        "third_party/boringssl/src/crypto/pem/pem_pkey.c",
+        "third_party/boringssl/src/crypto/pem/pem_x509.c",
+        "third_party/boringssl/src/crypto/pem/pem_xaux.c",
+        "third_party/boringssl/src/crypto/pkcs7/pkcs7.c",
+        "third_party/boringssl/src/crypto/pkcs7/pkcs7_x509.c",
+        "third_party/boringssl/src/crypto/pkcs8/p5_pbev2.c",
+        "third_party/boringssl/src/crypto/pkcs8/pkcs8.c",
+        "third_party/boringssl/src/crypto/pkcs8/pkcs8_x509.c",
+        "third_party/boringssl/src/crypto/poly1305/poly1305.c",
+        "third_party/boringssl/src/crypto/poly1305/poly1305_arm.c",
+        "third_party/boringssl/src/crypto/poly1305/poly1305_vec.c",
+        "third_party/boringssl/src/crypto/pool/pool.c",
+        "third_party/boringssl/src/crypto/rand_extra/deterministic.c",
+        "third_party/boringssl/src/crypto/rand_extra/forkunsafe.c",
+        "third_party/boringssl/src/crypto/rand_extra/fuchsia.c",
+        "third_party/boringssl/src/crypto/rand_extra/passive.c",
+        "third_party/boringssl/src/crypto/rand_extra/rand_extra.c",
+        "third_party/boringssl/src/crypto/rand_extra/windows.c",
+        "third_party/boringssl/src/crypto/rc4/rc4.c",
+        "third_party/boringssl/src/crypto/refcount_c11.c",
+        "third_party/boringssl/src/crypto/refcount_lock.c",
+        "third_party/boringssl/src/crypto/rsa_extra/rsa_asn1.c",
+        "third_party/boringssl/src/crypto/rsa_extra/rsa_print.c",
+        "third_party/boringssl/src/crypto/siphash/siphash.c",
+        "third_party/boringssl/src/crypto/stack/stack.c",
+        "third_party/boringssl/src/crypto/thread.c",
+        "third_party/boringssl/src/crypto/thread_none.c",
+        "third_party/boringssl/src/crypto/thread_pthread.c",
+        "third_party/boringssl/src/crypto/thread_win.c",
+        "third_party/boringssl/src/crypto/trust_token/pmbtoken.c",
+        "third_party/boringssl/src/crypto/trust_token/trust_token.c",
+        "third_party/boringssl/src/crypto/trust_token/voprf.c",
+        "third_party/boringssl/src/crypto/x509/a_digest.c",
+        "third_party/boringssl/src/crypto/x509/a_sign.c",
+        "third_party/boringssl/src/crypto/x509/a_verify.c",
+        "third_party/boringssl/src/crypto/x509/algorithm.c",
+        "third_party/boringssl/src/crypto/x509/asn1_gen.c",
+        "third_party/boringssl/src/crypto/x509/by_dir.c",
+        "third_party/boringssl/src/crypto/x509/by_file.c",
+        "third_party/boringssl/src/crypto/x509/i2d_pr.c",
+        "third_party/boringssl/src/crypto/x509/name_print.c",
+        "third_party/boringssl/src/crypto/x509/rsa_pss.c",
+        "third_party/boringssl/src/crypto/x509/t_crl.c",
+        "third_party/boringssl/src/crypto/x509/t_req.c",
+        "third_party/boringssl/src/crypto/x509/t_x509.c",
+        "third_party/boringssl/src/crypto/x509/t_x509a.c",
+        "third_party/boringssl/src/crypto/x509/x509.c",
+        "third_party/boringssl/src/crypto/x509/x509_att.c",
+        "third_party/boringssl/src/crypto/x509/x509_cmp.c",
+        "third_party/boringssl/src/crypto/x509/x509_d2.c",
+        "third_party/boringssl/src/crypto/x509/x509_def.c",
+        "third_party/boringssl/src/crypto/x509/x509_ext.c",
+        "third_party/boringssl/src/crypto/x509/x509_lu.c",
+        "third_party/boringssl/src/crypto/x509/x509_obj.c",
+        "third_party/boringssl/src/crypto/x509/x509_req.c",
+        "third_party/boringssl/src/crypto/x509/x509_set.c",
+        "third_party/boringssl/src/crypto/x509/x509_trs.c",
+        "third_party/boringssl/src/crypto/x509/x509_txt.c",
+        "third_party/boringssl/src/crypto/x509/x509_v3.c",
+        "third_party/boringssl/src/crypto/x509/x509_vfy.c",
+        "third_party/boringssl/src/crypto/x509/x509_vpm.c",
+        "third_party/boringssl/src/crypto/x509/x509cset.c",
+        "third_party/boringssl/src/crypto/x509/x509name.c",
+        "third_party/boringssl/src/crypto/x509/x509rset.c",
+        "third_party/boringssl/src/crypto/x509/x509spki.c",
+        "third_party/boringssl/src/crypto/x509/x_algor.c",
+        "third_party/boringssl/src/crypto/x509/x_all.c",
+        "third_party/boringssl/src/crypto/x509/x_attrib.c",
+        "third_party/boringssl/src/crypto/x509/x_crl.c",
+        "third_party/boringssl/src/crypto/x509/x_exten.c",
+        "third_party/boringssl/src/crypto/x509/x_info.c",
+        "third_party/boringssl/src/crypto/x509/x_name.c",
+        "third_party/boringssl/src/crypto/x509/x_pkey.c",
+        "third_party/boringssl/src/crypto/x509/x_pubkey.c",
+        "third_party/boringssl/src/crypto/x509/x_req.c",
+        "third_party/boringssl/src/crypto/x509/x_sig.c",
+        "third_party/boringssl/src/crypto/x509/x_spki.c",
+        "third_party/boringssl/src/crypto/x509/x_val.c",
+        "third_party/boringssl/src/crypto/x509/x_x509.c",
+        "third_party/boringssl/src/crypto/x509/x_x509a.c",
+        "third_party/boringssl/src/crypto/x509v3/pcy_cache.c",
+        "third_party/boringssl/src/crypto/x509v3/pcy_data.c",
+        "third_party/boringssl/src/crypto/x509v3/pcy_map.c",
+        "third_party/boringssl/src/crypto/x509v3/pcy_node.c",
+        "third_party/boringssl/src/crypto/x509v3/pcy_tree.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_akey.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_akeya.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_alt.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_bcons.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_bitst.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_conf.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_cpols.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_crld.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_enum.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_extku.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_genn.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_ia5.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_info.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_int.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_lib.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_ncons.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_ocsp.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_pci.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_pcia.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_pcons.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_pmaps.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_prn.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_purp.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_skey.c",
+        "third_party/boringssl/src/crypto/x509v3/v3_utl.c",
+        "third_party/boringssl/src/ssl/bio_ssl.cc",
+        "third_party/boringssl/src/ssl/d1_both.cc",
+        "third_party/boringssl/src/ssl/d1_lib.cc",
+        "third_party/boringssl/src/ssl/d1_pkt.cc",
+        "third_party/boringssl/src/ssl/d1_srtp.cc",
+        "third_party/boringssl/src/ssl/dtls_method.cc",
+        "third_party/boringssl/src/ssl/dtls_record.cc",
+        "third_party/boringssl/src/ssl/encrypted_client_hello.cc",
+        "third_party/boringssl/src/ssl/extensions.cc",
+        "third_party/boringssl/src/ssl/handoff.cc",
+        "third_party/boringssl/src/ssl/handshake.cc",
+        "third_party/boringssl/src/ssl/handshake_client.cc",
+        "third_party/boringssl/src/ssl/handshake_server.cc",
+        "third_party/boringssl/src/ssl/s3_both.cc",
+        "third_party/boringssl/src/ssl/s3_lib.cc",
+        "third_party/boringssl/src/ssl/s3_pkt.cc",
+        "third_party/boringssl/src/ssl/ssl_aead_ctx.cc",
+        "third_party/boringssl/src/ssl/ssl_asn1.cc",
+        "third_party/boringssl/src/ssl/ssl_buffer.cc",
+        "third_party/boringssl/src/ssl/ssl_cert.cc",
+        "third_party/boringssl/src/ssl/ssl_cipher.cc",
+        "third_party/boringssl/src/ssl/ssl_file.cc",
+        "third_party/boringssl/src/ssl/ssl_key_share.cc",
+        "third_party/boringssl/src/ssl/ssl_lib.cc",
+        "third_party/boringssl/src/ssl/ssl_privkey.cc",
+        "third_party/boringssl/src/ssl/ssl_session.cc",
+        "third_party/boringssl/src/ssl/ssl_stat.cc",
+        "third_party/boringssl/src/ssl/ssl_transcript.cc",
+        "third_party/boringssl/src/ssl/ssl_versions.cc",
+        "third_party/boringssl/src/ssl/ssl_x509.cc",
+        "third_party/boringssl/src/ssl/t1_enc.cc",
+        "third_party/boringssl/src/ssl/tls13_both.cc",
+        "third_party/boringssl/src/ssl/tls13_client.cc",
+        "third_party/boringssl/src/ssl/tls13_enc.cc",
+        "third_party/boringssl/src/ssl/tls13_server.cc",
+        "third_party/boringssl/src/ssl/tls_method.cc",
+        "third_party/boringssl/src/ssl/tls_record.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DBORINGSSL_ALLOW_CXX_RUNTIME",
+        "-DBORINGSSL_IMPLEMENTATION",
+        "-DBORINGSSL_NO_STATIC_INITIALIZER",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DOPENSSL_SMALL",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/boringssl:boringssl_asm
+cc_object {
+    name: "cronet_aml_third_party_boringssl_boringssl_asm",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            srcs: [
+                "third_party/boringssl/linux-arm/crypto/chacha/chacha-armv4.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/aesv8-armx32.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/armv4-mont.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/bsaes-armv7.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/ghash-armv4.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/sha1-armv4-large.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/sha256-armv4.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/sha512-armv4.S",
+                "third_party/boringssl/linux-arm/crypto/fipsmodule/vpaes-armv7.S",
+                "third_party/boringssl/linux-arm/crypto/test/trampoline-armv4.S",
+                "third_party/boringssl/src/crypto/curve25519/asm/x25519-asm-arm.S",
+                "third_party/boringssl/src/crypto/poly1305/poly1305_arm_asm.S",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                "third_party/boringssl/linux-aarch64/crypto/chacha/chacha-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/cipher_extra/chacha20_poly1305_armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/armv8-mont.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghash-neon-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/p256_beeu-armv8-asm.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha1-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha256-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/sha512-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/fipsmodule/vpaes-armv8.S",
+                "third_party/boringssl/linux-aarch64/crypto/test/trampoline-armv8.S",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                "third_party/boringssl/linux-x86/crypto/chacha/chacha-x86.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/aesni-x86.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/bn-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/co-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-ssse3-x86.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/ghash-x86.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/md5-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/sha1-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/sha256-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/sha512-586.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/vpaes-x86.S",
+                "third_party/boringssl/linux-x86/crypto/fipsmodule/x86-mont.S",
+                "third_party/boringssl/linux-x86/crypto/test/trampoline-x86.S",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                "third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S",
+                "third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S",
+                "third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S",
+            ],
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            srcs: [
+                "third_party/boringssl/linux-x86_64/crypto/chacha/chacha-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-ssse3-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/md5-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/p256_beeu-x86_64-asm.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont.S",
+                "third_party/boringssl/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S",
+                "third_party/boringssl/linux-x86_64/crypto/test/trampoline-x86_64.S",
+                "third_party/boringssl/src/crypto/hrss/asm/poly_rq_mul.S",
+            ],
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/boringssl/src/third_party/fiat:fiat_license
+cc_object {
+    name: "cronet_aml_third_party_boringssl_src_third_party_fiat_fiat_license",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/brotli:common
+cc_library_static {
+    name: "cronet_aml_third_party_brotli_common",
+    srcs: [
+        "third_party/brotli/common/constants.c",
+        "third_party/brotli/common/context.c",
+        "third_party/brotli/common/dictionary.c",
+        "third_party/brotli/common/platform.c",
+        "third_party/brotli/common/shared_dictionary.c",
+        "third_party/brotli/common/transform.c",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/brotli/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/brotli:dec
+cc_library_static {
+    name: "cronet_aml_third_party_brotli_dec",
+    srcs: [
+        "third_party/brotli/dec/bit_reader.c",
+        "third_party/brotli/dec/decode.c",
+        "third_party/brotli/dec/huffman.c",
+        "third_party/brotli/dec/state.c",
+    ],
+    static_libs: [
+        "cronet_aml_third_party_brotli_common",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/brotli/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/brotli:headers
+cc_object {
+    name: "cronet_aml_third_party_brotli_headers",
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/icu:icui18n
+cc_library_static {
+    name: "cronet_aml_third_party_icu_icui18n",
+    srcs: [
+        "third_party/icu/source/i18n/alphaindex.cpp",
+        "third_party/icu/source/i18n/anytrans.cpp",
+        "third_party/icu/source/i18n/astro.cpp",
+        "third_party/icu/source/i18n/basictz.cpp",
+        "third_party/icu/source/i18n/bocsu.cpp",
+        "third_party/icu/source/i18n/brktrans.cpp",
+        "third_party/icu/source/i18n/buddhcal.cpp",
+        "third_party/icu/source/i18n/calendar.cpp",
+        "third_party/icu/source/i18n/casetrn.cpp",
+        "third_party/icu/source/i18n/cecal.cpp",
+        "third_party/icu/source/i18n/chnsecal.cpp",
+        "third_party/icu/source/i18n/choicfmt.cpp",
+        "third_party/icu/source/i18n/coleitr.cpp",
+        "third_party/icu/source/i18n/coll.cpp",
+        "third_party/icu/source/i18n/collation.cpp",
+        "third_party/icu/source/i18n/collationbuilder.cpp",
+        "third_party/icu/source/i18n/collationcompare.cpp",
+        "third_party/icu/source/i18n/collationdata.cpp",
+        "third_party/icu/source/i18n/collationdatabuilder.cpp",
+        "third_party/icu/source/i18n/collationdatareader.cpp",
+        "third_party/icu/source/i18n/collationdatawriter.cpp",
+        "third_party/icu/source/i18n/collationfastlatin.cpp",
+        "third_party/icu/source/i18n/collationfastlatinbuilder.cpp",
+        "third_party/icu/source/i18n/collationfcd.cpp",
+        "third_party/icu/source/i18n/collationiterator.cpp",
+        "third_party/icu/source/i18n/collationkeys.cpp",
+        "third_party/icu/source/i18n/collationroot.cpp",
+        "third_party/icu/source/i18n/collationrootelements.cpp",
+        "third_party/icu/source/i18n/collationruleparser.cpp",
+        "third_party/icu/source/i18n/collationsets.cpp",
+        "third_party/icu/source/i18n/collationsettings.cpp",
+        "third_party/icu/source/i18n/collationtailoring.cpp",
+        "third_party/icu/source/i18n/collationweights.cpp",
+        "third_party/icu/source/i18n/compactdecimalformat.cpp",
+        "third_party/icu/source/i18n/coptccal.cpp",
+        "third_party/icu/source/i18n/cpdtrans.cpp",
+        "third_party/icu/source/i18n/csdetect.cpp",
+        "third_party/icu/source/i18n/csmatch.cpp",
+        "third_party/icu/source/i18n/csr2022.cpp",
+        "third_party/icu/source/i18n/csrecog.cpp",
+        "third_party/icu/source/i18n/csrmbcs.cpp",
+        "third_party/icu/source/i18n/csrsbcs.cpp",
+        "third_party/icu/source/i18n/csrucode.cpp",
+        "third_party/icu/source/i18n/csrutf8.cpp",
+        "third_party/icu/source/i18n/curramt.cpp",
+        "third_party/icu/source/i18n/currfmt.cpp",
+        "third_party/icu/source/i18n/currpinf.cpp",
+        "third_party/icu/source/i18n/currunit.cpp",
+        "third_party/icu/source/i18n/dangical.cpp",
+        "third_party/icu/source/i18n/datefmt.cpp",
+        "third_party/icu/source/i18n/dayperiodrules.cpp",
+        "third_party/icu/source/i18n/dcfmtsym.cpp",
+        "third_party/icu/source/i18n/decContext.cpp",
+        "third_party/icu/source/i18n/decNumber.cpp",
+        "third_party/icu/source/i18n/decimfmt.cpp",
+        "third_party/icu/source/i18n/double-conversion-bignum-dtoa.cpp",
+        "third_party/icu/source/i18n/double-conversion-bignum.cpp",
+        "third_party/icu/source/i18n/double-conversion-cached-powers.cpp",
+        "third_party/icu/source/i18n/double-conversion-double-to-string.cpp",
+        "third_party/icu/source/i18n/double-conversion-fast-dtoa.cpp",
+        "third_party/icu/source/i18n/double-conversion-string-to-double.cpp",
+        "third_party/icu/source/i18n/double-conversion-strtod.cpp",
+        "third_party/icu/source/i18n/dtfmtsym.cpp",
+        "third_party/icu/source/i18n/dtitvfmt.cpp",
+        "third_party/icu/source/i18n/dtitvinf.cpp",
+        "third_party/icu/source/i18n/dtptngen.cpp",
+        "third_party/icu/source/i18n/dtrule.cpp",
+        "third_party/icu/source/i18n/erarules.cpp",
+        "third_party/icu/source/i18n/esctrn.cpp",
+        "third_party/icu/source/i18n/ethpccal.cpp",
+        "third_party/icu/source/i18n/fmtable.cpp",
+        "third_party/icu/source/i18n/fmtable_cnv.cpp",
+        "third_party/icu/source/i18n/format.cpp",
+        "third_party/icu/source/i18n/formatted_string_builder.cpp",
+        "third_party/icu/source/i18n/formattedval_iterimpl.cpp",
+        "third_party/icu/source/i18n/formattedval_sbimpl.cpp",
+        "third_party/icu/source/i18n/formattedvalue.cpp",
+        "third_party/icu/source/i18n/fphdlimp.cpp",
+        "third_party/icu/source/i18n/fpositer.cpp",
+        "third_party/icu/source/i18n/funcrepl.cpp",
+        "third_party/icu/source/i18n/gender.cpp",
+        "third_party/icu/source/i18n/gregocal.cpp",
+        "third_party/icu/source/i18n/gregoimp.cpp",
+        "third_party/icu/source/i18n/hebrwcal.cpp",
+        "third_party/icu/source/i18n/indiancal.cpp",
+        "third_party/icu/source/i18n/inputext.cpp",
+        "third_party/icu/source/i18n/islamcal.cpp",
+        "third_party/icu/source/i18n/japancal.cpp",
+        "third_party/icu/source/i18n/listformatter.cpp",
+        "third_party/icu/source/i18n/measfmt.cpp",
+        "third_party/icu/source/i18n/measunit.cpp",
+        "third_party/icu/source/i18n/measunit_extra.cpp",
+        "third_party/icu/source/i18n/measure.cpp",
+        "third_party/icu/source/i18n/msgfmt.cpp",
+        "third_party/icu/source/i18n/name2uni.cpp",
+        "third_party/icu/source/i18n/nfrs.cpp",
+        "third_party/icu/source/i18n/nfrule.cpp",
+        "third_party/icu/source/i18n/nfsubs.cpp",
+        "third_party/icu/source/i18n/nortrans.cpp",
+        "third_party/icu/source/i18n/nultrans.cpp",
+        "third_party/icu/source/i18n/number_affixutils.cpp",
+        "third_party/icu/source/i18n/number_asformat.cpp",
+        "third_party/icu/source/i18n/number_capi.cpp",
+        "third_party/icu/source/i18n/number_compact.cpp",
+        "third_party/icu/source/i18n/number_currencysymbols.cpp",
+        "third_party/icu/source/i18n/number_decimalquantity.cpp",
+        "third_party/icu/source/i18n/number_decimfmtprops.cpp",
+        "third_party/icu/source/i18n/number_fluent.cpp",
+        "third_party/icu/source/i18n/number_formatimpl.cpp",
+        "third_party/icu/source/i18n/number_grouping.cpp",
+        "third_party/icu/source/i18n/number_integerwidth.cpp",
+        "third_party/icu/source/i18n/number_longnames.cpp",
+        "third_party/icu/source/i18n/number_mapper.cpp",
+        "third_party/icu/source/i18n/number_modifiers.cpp",
+        "third_party/icu/source/i18n/number_multiplier.cpp",
+        "third_party/icu/source/i18n/number_notation.cpp",
+        "third_party/icu/source/i18n/number_output.cpp",
+        "third_party/icu/source/i18n/number_padding.cpp",
+        "third_party/icu/source/i18n/number_patternmodifier.cpp",
+        "third_party/icu/source/i18n/number_patternstring.cpp",
+        "third_party/icu/source/i18n/number_rounding.cpp",
+        "third_party/icu/source/i18n/number_scientific.cpp",
+        "third_party/icu/source/i18n/number_skeletons.cpp",
+        "third_party/icu/source/i18n/number_symbolswrapper.cpp",
+        "third_party/icu/source/i18n/number_usageprefs.cpp",
+        "third_party/icu/source/i18n/number_utils.cpp",
+        "third_party/icu/source/i18n/numfmt.cpp",
+        "third_party/icu/source/i18n/numparse_affixes.cpp",
+        "third_party/icu/source/i18n/numparse_compositions.cpp",
+        "third_party/icu/source/i18n/numparse_currency.cpp",
+        "third_party/icu/source/i18n/numparse_decimal.cpp",
+        "third_party/icu/source/i18n/numparse_impl.cpp",
+        "third_party/icu/source/i18n/numparse_parsednumber.cpp",
+        "third_party/icu/source/i18n/numparse_scientific.cpp",
+        "third_party/icu/source/i18n/numparse_symbols.cpp",
+        "third_party/icu/source/i18n/numparse_validators.cpp",
+        "third_party/icu/source/i18n/numrange_capi.cpp",
+        "third_party/icu/source/i18n/numrange_fluent.cpp",
+        "third_party/icu/source/i18n/numrange_impl.cpp",
+        "third_party/icu/source/i18n/numsys.cpp",
+        "third_party/icu/source/i18n/olsontz.cpp",
+        "third_party/icu/source/i18n/persncal.cpp",
+        "third_party/icu/source/i18n/pluralranges.cpp",
+        "third_party/icu/source/i18n/plurfmt.cpp",
+        "third_party/icu/source/i18n/plurrule.cpp",
+        "third_party/icu/source/i18n/quant.cpp",
+        "third_party/icu/source/i18n/quantityformatter.cpp",
+        "third_party/icu/source/i18n/rbnf.cpp",
+        "third_party/icu/source/i18n/rbt.cpp",
+        "third_party/icu/source/i18n/rbt_data.cpp",
+        "third_party/icu/source/i18n/rbt_pars.cpp",
+        "third_party/icu/source/i18n/rbt_rule.cpp",
+        "third_party/icu/source/i18n/rbt_set.cpp",
+        "third_party/icu/source/i18n/rbtz.cpp",
+        "third_party/icu/source/i18n/regexcmp.cpp",
+        "third_party/icu/source/i18n/regeximp.cpp",
+        "third_party/icu/source/i18n/regexst.cpp",
+        "third_party/icu/source/i18n/regextxt.cpp",
+        "third_party/icu/source/i18n/region.cpp",
+        "third_party/icu/source/i18n/reldatefmt.cpp",
+        "third_party/icu/source/i18n/reldtfmt.cpp",
+        "third_party/icu/source/i18n/rematch.cpp",
+        "third_party/icu/source/i18n/remtrans.cpp",
+        "third_party/icu/source/i18n/repattrn.cpp",
+        "third_party/icu/source/i18n/rulebasedcollator.cpp",
+        "third_party/icu/source/i18n/scientificnumberformatter.cpp",
+        "third_party/icu/source/i18n/scriptset.cpp",
+        "third_party/icu/source/i18n/search.cpp",
+        "third_party/icu/source/i18n/selfmt.cpp",
+        "third_party/icu/source/i18n/sharedbreakiterator.cpp",
+        "third_party/icu/source/i18n/simpletz.cpp",
+        "third_party/icu/source/i18n/smpdtfmt.cpp",
+        "third_party/icu/source/i18n/smpdtfst.cpp",
+        "third_party/icu/source/i18n/sortkey.cpp",
+        "third_party/icu/source/i18n/standardplural.cpp",
+        "third_party/icu/source/i18n/string_segment.cpp",
+        "third_party/icu/source/i18n/strmatch.cpp",
+        "third_party/icu/source/i18n/strrepl.cpp",
+        "third_party/icu/source/i18n/stsearch.cpp",
+        "third_party/icu/source/i18n/taiwncal.cpp",
+        "third_party/icu/source/i18n/timezone.cpp",
+        "third_party/icu/source/i18n/titletrn.cpp",
+        "third_party/icu/source/i18n/tmunit.cpp",
+        "third_party/icu/source/i18n/tmutamt.cpp",
+        "third_party/icu/source/i18n/tmutfmt.cpp",
+        "third_party/icu/source/i18n/tolowtrn.cpp",
+        "third_party/icu/source/i18n/toupptrn.cpp",
+        "third_party/icu/source/i18n/translit.cpp",
+        "third_party/icu/source/i18n/transreg.cpp",
+        "third_party/icu/source/i18n/tridpars.cpp",
+        "third_party/icu/source/i18n/tzfmt.cpp",
+        "third_party/icu/source/i18n/tzgnames.cpp",
+        "third_party/icu/source/i18n/tznames.cpp",
+        "third_party/icu/source/i18n/tznames_impl.cpp",
+        "third_party/icu/source/i18n/tzrule.cpp",
+        "third_party/icu/source/i18n/tztrans.cpp",
+        "third_party/icu/source/i18n/ucal.cpp",
+        "third_party/icu/source/i18n/ucln_in.cpp",
+        "third_party/icu/source/i18n/ucol.cpp",
+        "third_party/icu/source/i18n/ucol_res.cpp",
+        "third_party/icu/source/i18n/ucol_sit.cpp",
+        "third_party/icu/source/i18n/ucoleitr.cpp",
+        "third_party/icu/source/i18n/ucsdet.cpp",
+        "third_party/icu/source/i18n/udat.cpp",
+        "third_party/icu/source/i18n/udateintervalformat.cpp",
+        "third_party/icu/source/i18n/udatpg.cpp",
+        "third_party/icu/source/i18n/ufieldpositer.cpp",
+        "third_party/icu/source/i18n/uitercollationiterator.cpp",
+        "third_party/icu/source/i18n/ulistformatter.cpp",
+        "third_party/icu/source/i18n/ulocdata.cpp",
+        "third_party/icu/source/i18n/umsg.cpp",
+        "third_party/icu/source/i18n/unesctrn.cpp",
+        "third_party/icu/source/i18n/uni2name.cpp",
+        "third_party/icu/source/i18n/units_complexconverter.cpp",
+        "third_party/icu/source/i18n/units_converter.cpp",
+        "third_party/icu/source/i18n/units_data.cpp",
+        "third_party/icu/source/i18n/units_router.cpp",
+        "third_party/icu/source/i18n/unum.cpp",
+        "third_party/icu/source/i18n/unumsys.cpp",
+        "third_party/icu/source/i18n/upluralrules.cpp",
+        "third_party/icu/source/i18n/uregex.cpp",
+        "third_party/icu/source/i18n/uregexc.cpp",
+        "third_party/icu/source/i18n/uregion.cpp",
+        "third_party/icu/source/i18n/usearch.cpp",
+        "third_party/icu/source/i18n/uspoof.cpp",
+        "third_party/icu/source/i18n/uspoof_build.cpp",
+        "third_party/icu/source/i18n/uspoof_conf.cpp",
+        "third_party/icu/source/i18n/uspoof_impl.cpp",
+        "third_party/icu/source/i18n/utf16collationiterator.cpp",
+        "third_party/icu/source/i18n/utf8collationiterator.cpp",
+        "third_party/icu/source/i18n/utmscale.cpp",
+        "third_party/icu/source/i18n/utrans.cpp",
+        "third_party/icu/source/i18n/vtzone.cpp",
+        "third_party/icu/source/i18n/vzone.cpp",
+        "third_party/icu/source/i18n/windtfmt.cpp",
+        "third_party/icu/source/i18n/winnmfmt.cpp",
+        "third_party/icu/source/i18n/wintzimpl.cpp",
+        "third_party/icu/source/i18n/zonemeta.cpp",
+        "third_party/icu/source/i18n/zrule.cpp",
+        "third_party/icu/source/i18n/ztrans.cpp",
+    ],
+    static_libs: [
+        "cronet_aml_third_party_icu_icuuc_private",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_DLOPEN=0",
+        "-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+        "-DUCONFIG_ONLY_HTML_CONVERSION=1",
+        "-DUCONFIG_USE_WINDOWS_LCID_MAPPING_API=0",
+        "-DUSE_CHROMIUM_ICU=1",
+        "-DU_CHARSET_IS_UTF8=1",
+        "-DU_ENABLE_DYLOAD=0",
+        "-DU_ENABLE_RESOURCE_TRACING=0",
+        "-DU_ENABLE_TRACING=1",
+        "-DU_I18N_IMPLEMENTATION",
+        "-DU_STATIC_IMPLEMENTATION",
+        "-DU_USING_ICU_NAMESPACE=0",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/icu/source/common/",
+        "third_party/icu/source/i18n/",
+    ],
+    cpp_std: "c++20",
+    rtti: true,
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/icu:icuuc_private
+cc_library_static {
+    name: "cronet_aml_third_party_icu_icuuc_private",
+    srcs: [
+        "third_party/icu/source/common/appendable.cpp",
+        "third_party/icu/source/common/bmpset.cpp",
+        "third_party/icu/source/common/brkeng.cpp",
+        "third_party/icu/source/common/brkiter.cpp",
+        "third_party/icu/source/common/bytesinkutil.cpp",
+        "third_party/icu/source/common/bytestream.cpp",
+        "third_party/icu/source/common/bytestrie.cpp",
+        "third_party/icu/source/common/bytestriebuilder.cpp",
+        "third_party/icu/source/common/bytestrieiterator.cpp",
+        "third_party/icu/source/common/caniter.cpp",
+        "third_party/icu/source/common/characterproperties.cpp",
+        "third_party/icu/source/common/chariter.cpp",
+        "third_party/icu/source/common/charstr.cpp",
+        "third_party/icu/source/common/cmemory.cpp",
+        "third_party/icu/source/common/cstr.cpp",
+        "third_party/icu/source/common/cstring.cpp",
+        "third_party/icu/source/common/cwchar.cpp",
+        "third_party/icu/source/common/dictbe.cpp",
+        "third_party/icu/source/common/dictionarydata.cpp",
+        "third_party/icu/source/common/dtintrv.cpp",
+        "third_party/icu/source/common/edits.cpp",
+        "third_party/icu/source/common/emojiprops.cpp",
+        "third_party/icu/source/common/errorcode.cpp",
+        "third_party/icu/source/common/filteredbrk.cpp",
+        "third_party/icu/source/common/filterednormalizer2.cpp",
+        "third_party/icu/source/common/icudataver.cpp",
+        "third_party/icu/source/common/icuplug.cpp",
+        "third_party/icu/source/common/loadednormalizer2impl.cpp",
+        "third_party/icu/source/common/localebuilder.cpp",
+        "third_party/icu/source/common/localematcher.cpp",
+        "third_party/icu/source/common/localeprioritylist.cpp",
+        "third_party/icu/source/common/locavailable.cpp",
+        "third_party/icu/source/common/locbased.cpp",
+        "third_party/icu/source/common/locdispnames.cpp",
+        "third_party/icu/source/common/locdistance.cpp",
+        "third_party/icu/source/common/locdspnm.cpp",
+        "third_party/icu/source/common/locid.cpp",
+        "third_party/icu/source/common/loclikely.cpp",
+        "third_party/icu/source/common/loclikelysubtags.cpp",
+        "third_party/icu/source/common/locmap.cpp",
+        "third_party/icu/source/common/locresdata.cpp",
+        "third_party/icu/source/common/locutil.cpp",
+        "third_party/icu/source/common/lsr.cpp",
+        "third_party/icu/source/common/lstmbe.cpp",
+        "third_party/icu/source/common/messagepattern.cpp",
+        "third_party/icu/source/common/normalizer2.cpp",
+        "third_party/icu/source/common/normalizer2impl.cpp",
+        "third_party/icu/source/common/normlzr.cpp",
+        "third_party/icu/source/common/parsepos.cpp",
+        "third_party/icu/source/common/patternprops.cpp",
+        "third_party/icu/source/common/pluralmap.cpp",
+        "third_party/icu/source/common/propname.cpp",
+        "third_party/icu/source/common/propsvec.cpp",
+        "third_party/icu/source/common/punycode.cpp",
+        "third_party/icu/source/common/putil.cpp",
+        "third_party/icu/source/common/rbbi.cpp",
+        "third_party/icu/source/common/rbbi_cache.cpp",
+        "third_party/icu/source/common/rbbidata.cpp",
+        "third_party/icu/source/common/rbbinode.cpp",
+        "third_party/icu/source/common/rbbirb.cpp",
+        "third_party/icu/source/common/rbbiscan.cpp",
+        "third_party/icu/source/common/rbbisetb.cpp",
+        "third_party/icu/source/common/rbbistbl.cpp",
+        "third_party/icu/source/common/rbbitblb.cpp",
+        "third_party/icu/source/common/resbund.cpp",
+        "third_party/icu/source/common/resbund_cnv.cpp",
+        "third_party/icu/source/common/resource.cpp",
+        "third_party/icu/source/common/restrace.cpp",
+        "third_party/icu/source/common/ruleiter.cpp",
+        "third_party/icu/source/common/schriter.cpp",
+        "third_party/icu/source/common/serv.cpp",
+        "third_party/icu/source/common/servlk.cpp",
+        "third_party/icu/source/common/servlkf.cpp",
+        "third_party/icu/source/common/servls.cpp",
+        "third_party/icu/source/common/servnotf.cpp",
+        "third_party/icu/source/common/servrbf.cpp",
+        "third_party/icu/source/common/servslkf.cpp",
+        "third_party/icu/source/common/sharedobject.cpp",
+        "third_party/icu/source/common/simpleformatter.cpp",
+        "third_party/icu/source/common/static_unicode_sets.cpp",
+        "third_party/icu/source/common/stringpiece.cpp",
+        "third_party/icu/source/common/stringtriebuilder.cpp",
+        "third_party/icu/source/common/uarrsort.cpp",
+        "third_party/icu/source/common/ubidi.cpp",
+        "third_party/icu/source/common/ubidi_props.cpp",
+        "third_party/icu/source/common/ubidiln.cpp",
+        "third_party/icu/source/common/ubiditransform.cpp",
+        "third_party/icu/source/common/ubidiwrt.cpp",
+        "third_party/icu/source/common/ubrk.cpp",
+        "third_party/icu/source/common/ucase.cpp",
+        "third_party/icu/source/common/ucasemap.cpp",
+        "third_party/icu/source/common/ucasemap_titlecase_brkiter.cpp",
+        "third_party/icu/source/common/ucat.cpp",
+        "third_party/icu/source/common/uchar.cpp",
+        "third_party/icu/source/common/ucharstrie.cpp",
+        "third_party/icu/source/common/ucharstriebuilder.cpp",
+        "third_party/icu/source/common/ucharstrieiterator.cpp",
+        "third_party/icu/source/common/uchriter.cpp",
+        "third_party/icu/source/common/ucln_cmn.cpp",
+        "third_party/icu/source/common/ucmndata.cpp",
+        "third_party/icu/source/common/ucnv.cpp",
+        "third_party/icu/source/common/ucnv2022.cpp",
+        "third_party/icu/source/common/ucnv_bld.cpp",
+        "third_party/icu/source/common/ucnv_cb.cpp",
+        "third_party/icu/source/common/ucnv_cnv.cpp",
+        "third_party/icu/source/common/ucnv_ct.cpp",
+        "third_party/icu/source/common/ucnv_err.cpp",
+        "third_party/icu/source/common/ucnv_ext.cpp",
+        "third_party/icu/source/common/ucnv_io.cpp",
+        "third_party/icu/source/common/ucnv_lmb.cpp",
+        "third_party/icu/source/common/ucnv_set.cpp",
+        "third_party/icu/source/common/ucnv_u16.cpp",
+        "third_party/icu/source/common/ucnv_u32.cpp",
+        "third_party/icu/source/common/ucnv_u7.cpp",
+        "third_party/icu/source/common/ucnv_u8.cpp",
+        "third_party/icu/source/common/ucnvbocu.cpp",
+        "third_party/icu/source/common/ucnvdisp.cpp",
+        "third_party/icu/source/common/ucnvhz.cpp",
+        "third_party/icu/source/common/ucnvisci.cpp",
+        "third_party/icu/source/common/ucnvlat1.cpp",
+        "third_party/icu/source/common/ucnvmbcs.cpp",
+        "third_party/icu/source/common/ucnvscsu.cpp",
+        "third_party/icu/source/common/ucnvsel.cpp",
+        "third_party/icu/source/common/ucol_swp.cpp",
+        "third_party/icu/source/common/ucptrie.cpp",
+        "third_party/icu/source/common/ucurr.cpp",
+        "third_party/icu/source/common/udata.cpp",
+        "third_party/icu/source/common/udatamem.cpp",
+        "third_party/icu/source/common/udataswp.cpp",
+        "third_party/icu/source/common/uenum.cpp",
+        "third_party/icu/source/common/uhash.cpp",
+        "third_party/icu/source/common/uhash_us.cpp",
+        "third_party/icu/source/common/uidna.cpp",
+        "third_party/icu/source/common/uinit.cpp",
+        "third_party/icu/source/common/uinvchar.cpp",
+        "third_party/icu/source/common/uiter.cpp",
+        "third_party/icu/source/common/ulist.cpp",
+        "third_party/icu/source/common/uloc.cpp",
+        "third_party/icu/source/common/uloc_keytype.cpp",
+        "third_party/icu/source/common/uloc_tag.cpp",
+        "third_party/icu/source/common/umapfile.cpp",
+        "third_party/icu/source/common/umath.cpp",
+        "third_party/icu/source/common/umutablecptrie.cpp",
+        "third_party/icu/source/common/umutex.cpp",
+        "third_party/icu/source/common/unames.cpp",
+        "third_party/icu/source/common/unifiedcache.cpp",
+        "third_party/icu/source/common/unifilt.cpp",
+        "third_party/icu/source/common/unifunct.cpp",
+        "third_party/icu/source/common/uniset.cpp",
+        "third_party/icu/source/common/uniset_closure.cpp",
+        "third_party/icu/source/common/uniset_props.cpp",
+        "third_party/icu/source/common/unisetspan.cpp",
+        "third_party/icu/source/common/unistr.cpp",
+        "third_party/icu/source/common/unistr_case.cpp",
+        "third_party/icu/source/common/unistr_case_locale.cpp",
+        "third_party/icu/source/common/unistr_cnv.cpp",
+        "third_party/icu/source/common/unistr_props.cpp",
+        "third_party/icu/source/common/unistr_titlecase_brkiter.cpp",
+        "third_party/icu/source/common/unorm.cpp",
+        "third_party/icu/source/common/unormcmp.cpp",
+        "third_party/icu/source/common/uobject.cpp",
+        "third_party/icu/source/common/uprops.cpp",
+        "third_party/icu/source/common/ures_cnv.cpp",
+        "third_party/icu/source/common/uresbund.cpp",
+        "third_party/icu/source/common/uresdata.cpp",
+        "third_party/icu/source/common/usc_impl.cpp",
+        "third_party/icu/source/common/uscript.cpp",
+        "third_party/icu/source/common/uscript_props.cpp",
+        "third_party/icu/source/common/uset.cpp",
+        "third_party/icu/source/common/uset_props.cpp",
+        "third_party/icu/source/common/usetiter.cpp",
+        "third_party/icu/source/common/ushape.cpp",
+        "third_party/icu/source/common/usprep.cpp",
+        "third_party/icu/source/common/ustack.cpp",
+        "third_party/icu/source/common/ustr_cnv.cpp",
+        "third_party/icu/source/common/ustr_titlecase_brkiter.cpp",
+        "third_party/icu/source/common/ustr_wcs.cpp",
+        "third_party/icu/source/common/ustrcase.cpp",
+        "third_party/icu/source/common/ustrcase_locale.cpp",
+        "third_party/icu/source/common/ustrenum.cpp",
+        "third_party/icu/source/common/ustrfmt.cpp",
+        "third_party/icu/source/common/ustring.cpp",
+        "third_party/icu/source/common/ustrtrns.cpp",
+        "third_party/icu/source/common/utext.cpp",
+        "third_party/icu/source/common/utf_impl.cpp",
+        "third_party/icu/source/common/util.cpp",
+        "third_party/icu/source/common/util_props.cpp",
+        "third_party/icu/source/common/utrace.cpp",
+        "third_party/icu/source/common/utrie.cpp",
+        "third_party/icu/source/common/utrie2.cpp",
+        "third_party/icu/source/common/utrie2_builder.cpp",
+        "third_party/icu/source/common/utrie_swap.cpp",
+        "third_party/icu/source/common/uts46.cpp",
+        "third_party/icu/source/common/utypes.cpp",
+        "third_party/icu/source/common/uvector.cpp",
+        "third_party/icu/source/common/uvectr32.cpp",
+        "third_party/icu/source/common/uvectr64.cpp",
+        "third_party/icu/source/common/wintz.cpp",
+        "third_party/icu/source/stubdata/stubdata.cpp",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_DLOPEN=0",
+        "-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+        "-DUCONFIG_ONLY_HTML_CONVERSION=1",
+        "-DUCONFIG_USE_WINDOWS_LCID_MAPPING_API=0",
+        "-DUSE_CHROMIUM_ICU=1",
+        "-DU_CHARSET_IS_UTF8=1",
+        "-DU_COMMON_IMPLEMENTATION",
+        "-DU_ENABLE_DYLOAD=0",
+        "-DU_ENABLE_RESOURCE_TRACING=0",
+        "-DU_ENABLE_TRACING=1",
+        "-DU_ICUDATAENTRY_IN_COMMON",
+        "-DU_STATIC_IMPLEMENTATION",
+        "-DU_USING_ICU_NAMESPACE=0",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/icu/source/common/",
+        "third_party/icu/source/i18n/",
+    ],
+    cpp_std: "c++20",
+    rtti: true,
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/icu:icuuc_public
+cc_object {
+    name: "cronet_aml_third_party_icu_icuuc_public",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/libevent:libevent
+cc_library_static {
+    name: "cronet_aml_third_party_libevent_libevent",
+    srcs: [
+        "third_party/libevent/buffer.c",
+        "third_party/libevent/epoll.c",
+        "third_party/libevent/evbuffer.c",
+        "third_party/libevent/evdns.c",
+        "third_party/libevent/event.c",
+        "third_party/libevent/event_tagging.c",
+        "third_party/libevent/evrpc.c",
+        "third_party/libevent/evutil.c",
+        "third_party/libevent/http.c",
+        "third_party/libevent/log.c",
+        "third_party/libevent/poll.c",
+        "third_party/libevent/select.c",
+        "third_party/libevent/signal.c",
+        "third_party/libevent/strlcpy.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_CONFIG_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            local_include_dirs: [
+                "third_party/libevent/android/",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+            local_include_dirs: [
+                "third_party/libevent/android/",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/libevent/android/",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/libevent/android/",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/libevent/linux/",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/metrics_proto:metrics_proto
+cc_genrule {
+    name: "cronet_aml_third_party_metrics_proto_metrics_proto_gen",
+    srcs: [
+        "third_party/metrics_proto/call_stack_profile.proto",
+        "third_party/metrics_proto/cast_logs.proto",
+        "third_party/metrics_proto/chrome_os_app_list_launch_event.proto",
+        "third_party/metrics_proto/chrome_searchbox_stats.proto",
+        "third_party/metrics_proto/chrome_user_metrics_extension.proto",
+        "third_party/metrics_proto/custom_tab_session.proto",
+        "third_party/metrics_proto/execution_context.proto",
+        "third_party/metrics_proto/extension_install.proto",
+        "third_party/metrics_proto/histogram_event.proto",
+        "third_party/metrics_proto/omnibox_event.proto",
+        "third_party/metrics_proto/omnibox_focus_type.proto",
+        "third_party/metrics_proto/omnibox_input_type.proto",
+        "third_party/metrics_proto/perf_data.proto",
+        "third_party/metrics_proto/perf_stat.proto",
+        "third_party/metrics_proto/printer_event.proto",
+        "third_party/metrics_proto/reporting_info.proto",
+        "third_party/metrics_proto/sampled_profile.proto",
+        "third_party/metrics_proto/structured_data.proto",
+        "third_party/metrics_proto/system_profile.proto",
+        "third_party/metrics_proto/trace_log.proto",
+        "third_party/metrics_proto/translate_event.proto",
+        "third_party/metrics_proto/ukm/aggregate.proto",
+        "third_party/metrics_proto/ukm/entry.proto",
+        "third_party/metrics_proto/ukm/report.proto",
+        "third_party/metrics_proto/ukm/source.proto",
+        "third_party/metrics_proto/user_action_event.proto",
+        "third_party/metrics_proto/user_demographics.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/third_party/metrics_proto --cpp_out=lite=true:$(genDir)/external/chromium_org/third_party/metrics_proto/ $(in)",
+    out: [
+        "external/chromium_org/third_party/metrics_proto/call_stack_profile.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/cast_logs.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/chrome_os_app_list_launch_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/chrome_searchbox_stats.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/chrome_user_metrics_extension.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/custom_tab_session.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/execution_context.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/extension_install.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/histogram_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/omnibox_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/omnibox_focus_type.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/omnibox_input_type.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/perf_data.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/perf_stat.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/printer_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/reporting_info.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/sampled_profile.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/structured_data.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/system_profile.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/trace_log.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/translate_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/ukm/aggregate.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/ukm/entry.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/ukm/report.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/ukm/source.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/user_action_event.pb.cc",
+        "external/chromium_org/third_party/metrics_proto/user_demographics.pb.cc",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //third_party/metrics_proto:metrics_proto
+cc_genrule {
+    name: "cronet_aml_third_party_metrics_proto_metrics_proto_gen_headers",
+    srcs: [
+        "third_party/metrics_proto/call_stack_profile.proto",
+        "third_party/metrics_proto/cast_logs.proto",
+        "third_party/metrics_proto/chrome_os_app_list_launch_event.proto",
+        "third_party/metrics_proto/chrome_searchbox_stats.proto",
+        "third_party/metrics_proto/chrome_user_metrics_extension.proto",
+        "third_party/metrics_proto/custom_tab_session.proto",
+        "third_party/metrics_proto/execution_context.proto",
+        "third_party/metrics_proto/extension_install.proto",
+        "third_party/metrics_proto/histogram_event.proto",
+        "third_party/metrics_proto/omnibox_event.proto",
+        "third_party/metrics_proto/omnibox_focus_type.proto",
+        "third_party/metrics_proto/omnibox_input_type.proto",
+        "third_party/metrics_proto/perf_data.proto",
+        "third_party/metrics_proto/perf_stat.proto",
+        "third_party/metrics_proto/printer_event.proto",
+        "third_party/metrics_proto/reporting_info.proto",
+        "third_party/metrics_proto/sampled_profile.proto",
+        "third_party/metrics_proto/structured_data.proto",
+        "third_party/metrics_proto/system_profile.proto",
+        "third_party/metrics_proto/trace_log.proto",
+        "third_party/metrics_proto/translate_event.proto",
+        "third_party/metrics_proto/ukm/aggregate.proto",
+        "third_party/metrics_proto/ukm/entry.proto",
+        "third_party/metrics_proto/ukm/report.proto",
+        "third_party/metrics_proto/ukm/source.proto",
+        "third_party/metrics_proto/user_action_event.proto",
+        "third_party/metrics_proto/user_demographics.proto",
+    ],
+    tools: [
+        "cronet_aml_third_party_protobuf_protoc",
+    ],
+    cmd: "$(location cronet_aml_third_party_protobuf_protoc) --proto_path=external/chromium_org/third_party/metrics_proto --cpp_out=lite=true:$(genDir)/external/chromium_org/third_party/metrics_proto/ $(in)",
+    out: [
+        "external/chromium_org/third_party/metrics_proto/call_stack_profile.pb.h",
+        "external/chromium_org/third_party/metrics_proto/cast_logs.pb.h",
+        "external/chromium_org/third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/chrome_searchbox_stats.pb.h",
+        "external/chromium_org/third_party/metrics_proto/chrome_user_metrics_extension.pb.h",
+        "external/chromium_org/third_party/metrics_proto/custom_tab_session.pb.h",
+        "external/chromium_org/third_party/metrics_proto/execution_context.pb.h",
+        "external/chromium_org/third_party/metrics_proto/extension_install.pb.h",
+        "external/chromium_org/third_party/metrics_proto/histogram_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/omnibox_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/omnibox_focus_type.pb.h",
+        "external/chromium_org/third_party/metrics_proto/omnibox_input_type.pb.h",
+        "external/chromium_org/third_party/metrics_proto/perf_data.pb.h",
+        "external/chromium_org/third_party/metrics_proto/perf_stat.pb.h",
+        "external/chromium_org/third_party/metrics_proto/printer_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/reporting_info.pb.h",
+        "external/chromium_org/third_party/metrics_proto/sampled_profile.pb.h",
+        "external/chromium_org/third_party/metrics_proto/structured_data.pb.h",
+        "external/chromium_org/third_party/metrics_proto/system_profile.pb.h",
+        "external/chromium_org/third_party/metrics_proto/trace_log.pb.h",
+        "external/chromium_org/third_party/metrics_proto/translate_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/ukm/aggregate.pb.h",
+        "external/chromium_org/third_party/metrics_proto/ukm/entry.pb.h",
+        "external/chromium_org/third_party/metrics_proto/ukm/report.pb.h",
+        "external/chromium_org/third_party/metrics_proto/ukm/source.pb.h",
+        "external/chromium_org/third_party/metrics_proto/user_action_event.pb.h",
+        "external/chromium_org/third_party/metrics_proto/user_demographics.pb.h",
+    ],
+    export_include_dirs: [
+        ".",
+        "protos",
+        "third_party/metrics_proto",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //third_party/modp_b64:modp_b64
+cc_library_static {
+    name: "cronet_aml_third_party_modp_b64_modp_b64",
+    srcs: [
+        "third_party/modp_b64/modp_b64.cc",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/protobuf:protobuf_full
+cc_library_static {
+    name: "cronet_aml_third_party_protobuf_protobuf_full",
+    srcs: [
+        "third_party/protobuf/src/google/protobuf/any.cc",
+        "third_party/protobuf/src/google/protobuf/any.pb.cc",
+        "third_party/protobuf/src/google/protobuf/any_lite.cc",
+        "third_party/protobuf/src/google/protobuf/api.pb.cc",
+        "third_party/protobuf/src/google/protobuf/arena.cc",
+        "third_party/protobuf/src/google/protobuf/arenastring.cc",
+        "third_party/protobuf/src/google/protobuf/arenaz_sampler.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/importer.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/parser.cc",
+        "third_party/protobuf/src/google/protobuf/descriptor.cc",
+        "third_party/protobuf/src/google/protobuf/descriptor.pb.cc",
+        "third_party/protobuf/src/google/protobuf/descriptor_database.cc",
+        "third_party/protobuf/src/google/protobuf/duration.pb.cc",
+        "third_party/protobuf/src/google/protobuf/dynamic_message.cc",
+        "third_party/protobuf/src/google/protobuf/empty.pb.cc",
+        "third_party/protobuf/src/google/protobuf/extension_set.cc",
+        "third_party/protobuf/src/google/protobuf/extension_set_heavy.cc",
+        "third_party/protobuf/src/google/protobuf/field_mask.pb.cc",
+        "third_party/protobuf/src/google/protobuf/generated_enum_util.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_bases.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_reflection.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_tctable_full.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_tctable_lite.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_util.cc",
+        "third_party/protobuf/src/google/protobuf/implicit_weak_message.cc",
+        "third_party/protobuf/src/google/protobuf/inlined_string_field.cc",
+        "third_party/protobuf/src/google/protobuf/io/coded_stream.cc",
+        "third_party/protobuf/src/google/protobuf/io/gzip_stream.cc",
+        "third_party/protobuf/src/google/protobuf/io/io_win32.cc",
+        "third_party/protobuf/src/google/protobuf/io/printer.cc",
+        "third_party/protobuf/src/google/protobuf/io/strtod.cc",
+        "third_party/protobuf/src/google/protobuf/io/tokenizer.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
+        "third_party/protobuf/src/google/protobuf/map.cc",
+        "third_party/protobuf/src/google/protobuf/map_field.cc",
+        "third_party/protobuf/src/google/protobuf/message.cc",
+        "third_party/protobuf/src/google/protobuf/message_lite.cc",
+        "third_party/protobuf/src/google/protobuf/parse_context.cc",
+        "third_party/protobuf/src/google/protobuf/reflection_ops.cc",
+        "third_party/protobuf/src/google/protobuf/repeated_field.cc",
+        "third_party/protobuf/src/google/protobuf/repeated_ptr_field.cc",
+        "third_party/protobuf/src/google/protobuf/service.cc",
+        "third_party/protobuf/src/google/protobuf/source_context.pb.cc",
+        "third_party/protobuf/src/google/protobuf/struct.pb.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/bytestream.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/common.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/int128.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/status.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/statusor.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/strutil.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/substitute.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/time.cc",
+        "third_party/protobuf/src/google/protobuf/text_format.cc",
+        "third_party/protobuf/src/google/protobuf/timestamp.pb.cc",
+        "third_party/protobuf/src/google/protobuf/type.pb.cc",
+        "third_party/protobuf/src/google/protobuf/unknown_field_set.cc",
+        "third_party/protobuf/src/google/protobuf/util/delimited_message_util.cc",
+        "third_party/protobuf/src/google/protobuf/util/field_comparator.cc",
+        "third_party/protobuf/src/google/protobuf/util/field_mask_util.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/datapiece.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/default_value_objectwriter.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/error_listener.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/field_mask_utility.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/json_escaping.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/json_objectwriter.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/object_writer.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/proto_writer.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/type_info.cc",
+        "third_party/protobuf/src/google/protobuf/util/internal/utility.cc",
+        "third_party/protobuf/src/google/protobuf/util/json_util.cc",
+        "third_party/protobuf/src/google/protobuf/util/message_differencer.cc",
+        "third_party/protobuf/src/google/protobuf/util/time_util.cc",
+        "third_party/protobuf/src/google/protobuf/util/type_resolver_util.cc",
+        "third_party/protobuf/src/google/protobuf/wire_format.cc",
+        "third_party/protobuf/src/google/protobuf/wire_format_lite.cc",
+        "third_party/protobuf/src/google/protobuf/wrappers.pb.cc",
+    ],
+    static_libs: [
+        "cronet_aml_third_party_zlib_zlib",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_ZLIB",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/protobuf/src/",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //third_party/protobuf:protobuf_lite
+cc_library_static {
+    name: "cronet_aml_third_party_protobuf_protobuf_lite",
+    srcs: [
+        "third_party/protobuf/src/google/protobuf/any_lite.cc",
+        "third_party/protobuf/src/google/protobuf/arena.cc",
+        "third_party/protobuf/src/google/protobuf/arenastring.cc",
+        "third_party/protobuf/src/google/protobuf/arenaz_sampler.cc",
+        "third_party/protobuf/src/google/protobuf/extension_set.cc",
+        "third_party/protobuf/src/google/protobuf/generated_enum_util.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_tctable_lite.cc",
+        "third_party/protobuf/src/google/protobuf/generated_message_util.cc",
+        "third_party/protobuf/src/google/protobuf/implicit_weak_message.cc",
+        "third_party/protobuf/src/google/protobuf/inlined_string_field.cc",
+        "third_party/protobuf/src/google/protobuf/io/coded_stream.cc",
+        "third_party/protobuf/src/google/protobuf/io/io_win32.cc",
+        "third_party/protobuf/src/google/protobuf/io/strtod.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl.cc",
+        "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
+        "third_party/protobuf/src/google/protobuf/map.cc",
+        "third_party/protobuf/src/google/protobuf/message_lite.cc",
+        "third_party/protobuf/src/google/protobuf/parse_context.cc",
+        "third_party/protobuf/src/google/protobuf/repeated_field.cc",
+        "third_party/protobuf/src/google/protobuf/repeated_ptr_field.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/bytestream.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/common.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/int128.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/status.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/statusor.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/strutil.cc",
+        "third_party/protobuf/src/google/protobuf/stubs/time.cc",
+        "third_party/protobuf/src/google/protobuf/wire_format_lite.cc",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DHAVE_SYS_UIO_H",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/protobuf:protoc
+cc_binary {
+    name: "cronet_aml_third_party_protobuf_protoc",
+    srcs: [
+        ":cronet_aml_buildtools_third_party_libc___libc__",
+        ":cronet_aml_buildtools_third_party_libc__abi_libc__abi",
+        "third_party/protobuf/src/google/protobuf/compiler/main.cc",
+    ],
+    static_libs: [
+        "cronet_aml_third_party_protobuf_protobuf_full",
+        "cronet_aml_third_party_protobuf_protoc_lib",
+        "cronet_aml_third_party_zlib_zlib",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //third_party/protobuf:protoc_lib
+cc_library_static {
+    name: "cronet_aml_third_party_protobuf_protoc_lib",
+    srcs: [
+        "third_party/protobuf/src/google/protobuf/compiler/code_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_enum.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_enum_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_extension.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_file.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_map_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_parse_function_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_service.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_string_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_enum.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_field_base.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_map_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_message.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_message_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_context.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_doc_comment.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_enum.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_enum_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_enum_field_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_enum_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_extension.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_extension_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_file.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_generator_factory.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_helpers.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_kotlin_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_map_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_map_field_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message_builder.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message_builder_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message_field_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_message_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_name_resolver.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_primitive_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_primitive_field_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_service.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_shared_code_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_string_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/java/java_string_field_lite.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/js/well_known_types_embed.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_extension.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_file.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_message.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/php/php_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/plugin.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/plugin.pb.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/python/python_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/python/python_helpers.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/python/python_pyi_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/ruby/ruby_generator.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/subprocess.cc",
+        "third_party/protobuf/src/google/protobuf/compiler/zip_writer.cc",
+    ],
+    static_libs: [
+        "cronet_aml_third_party_protobuf_protobuf_full",
+        "cronet_aml_third_party_zlib_zlib",
+    ],
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
+        "-DGOOGLE_PROTOBUF_NO_RTTI",
+        "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+        "-DHAVE_PTHREAD",
+        "-DUSE_AURA=1",
+        "-DUSE_OZONE=1",
+        "-DUSE_UDEV",
+        "-D_DEBUG",
+        "-D_FILE_OFFSET_BITS=64",
+        "-D_GNU_SOURCE",
+        "-D_LARGEFILE64_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-msse3",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/protobuf/src/",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //third_party/zlib:zlib
+cc_library_static {
+    name: "cronet_aml_third_party_zlib_zlib",
+    srcs: [
+        ":cronet_aml_third_party_zlib_zlib_adler32_simd",
+        ":cronet_aml_third_party_zlib_zlib_inflate_chunk_simd",
+        "third_party/zlib/adler32.c",
+        "third_party/zlib/compress.c",
+        "third_party/zlib/cpu_features.c",
+        "third_party/zlib/crc32.c",
+        "third_party/zlib/deflate.c",
+        "third_party/zlib/gzclose.c",
+        "third_party/zlib/gzlib.c",
+        "third_party/zlib/gzread.c",
+        "third_party/zlib/gzwrite.c",
+        "third_party/zlib/infback.c",
+        "third_party/zlib/inffast.c",
+        "third_party/zlib/inftrees.c",
+        "third_party/zlib/trees.c",
+        "third_party/zlib/uncompr.c",
+        "third_party/zlib/zutil.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_zlib_zlib_arm_crc32",
+            ],
+            cflags: [
+                "-DADLER32_SIMD_NEON",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DARMV8_OS_ANDROID",
+                "-DCRC32_ARMV8_CRC32",
+                "-DDEFLATE_SLIDE_HASH_NEON",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_SIMD_NEON",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+        },
+        android_arm64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_zlib_zlib_arm_crc32",
+            ],
+            cflags: [
+                "-DADLER32_SIMD_NEON",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DARMV8_OS_ANDROID",
+                "-DCRC32_ARMV8_CRC32",
+                "-DDEFLATE_SLIDE_HASH_NEON",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_NEON",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+        },
+        android_x86: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_zlib_zlib_crc32_simd",
+            ],
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DCRC32_SIMD_SSE42_PCLMUL",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-DX86_NOT_WINDOWS",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+        },
+        android_x86_64: {
+            srcs: [
+                ":cronet_aml_third_party_android_ndk_cpu_features",
+                ":cronet_aml_third_party_zlib_zlib_crc32_simd",
+            ],
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DCRC32_SIMD_SSE42_PCLMUL",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-DX86_NOT_WINDOWS",
+                "-msse3",
+            ],
+            local_include_dirs: [
+                "third_party/android_ndk/sources/android/cpufeatures/",
+            ],
+        },
+        host: {
+            srcs: [
+                ":cronet_aml_third_party_zlib_zlib_crc32_simd",
+            ],
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DCRC32_SIMD_SSE42_PCLMUL",
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-DX86_NOT_WINDOWS",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/zlib:zlib_adler32_simd
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_adler32_simd",
+    srcs: [
+        "third_party/zlib/adler32_simd.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DADLER32_SIMD_NEON",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DADLER32_SIMD_NEON",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DX86_NOT_WINDOWS",
+                "-msse3",
+                "-mssse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DX86_NOT_WINDOWS",
+                "-msse3",
+                "-mssse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DADLER32_SIMD_SSSE3",
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-DX86_NOT_WINDOWS",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+                "-mssse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/zlib:zlib_arm_crc32
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_arm_crc32",
+    srcs: [
+        "third_party/zlib/crc32_simd.c",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DARMV8_OS_ANDROID",
+        "-DCRC32_ARMV8_CRC32",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+}
+
+// GN: //third_party/zlib:zlib_common_headers
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_common_headers",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/zlib:zlib_crc32_simd
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_crc32_simd",
+    srcs: [
+        "third_party/zlib/crc32_simd.c",
+        "third_party/zlib/crc_folding.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCRC32_SIMD_SSE42_PCLMUL",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+        "-mpclmul",
+        "-msse3",
+        "-msse4.2",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/zlib:zlib_inflate_chunk_simd
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_inflate_chunk_simd",
+    srcs: [
+        "third_party/zlib/contrib/optimizations/inffast_chunk.c",
+        "third_party/zlib/contrib/optimizations/inflate.c",
+    ],
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/zlib/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_SIMD_NEON",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_NEON",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DHAVE_SYS_UIO_H",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DINFLATE_CHUNK_READ_64LE",
+                "-DINFLATE_CHUNK_SIMD_SSE2",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //third_party/zlib:zlib_slide_hash_simd
+cc_object {
+    name: "cronet_aml_third_party_zlib_zlib_slide_hash_simd",
+    host_supported: true,
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DZLIB_DEBUG",
+        "-DZLIB_IMPLEMENTATION",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DDEFLATE_SLIDE_HASH_NEON",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DDEFLATE_SLIDE_HASH_NEON",
+                "-DHAVE_SYS_UIO_H",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-DANDROID",
+                "-DANDROID_NDK_VERSION_ROLL=r23_1",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DHAVE_SYS_UIO_H",
+                "-msse3",
+            ],
+        },
+        host: {
+            cflags: [
+                "-DCR_SYSROOT_KEY=20220331T153654Z-0",
+                "-DDEFLATE_SLIDE_HASH_SSE2",
+                "-DUSE_AURA=1",
+                "-DUSE_OZONE=1",
+                "-DUSE_UDEV",
+                "-D_FILE_OFFSET_BITS=64",
+                "-D_LARGEFILE64_SOURCE",
+                "-D_LARGEFILE_SOURCE",
+                "-msse3",
+            ],
+        },
+    },
+}
+
+// GN: //url:buildflags__android_arm
+cc_genrule {
+    name: "cronet_aml_url_buildflags__android_arm",
+    cmd: "echo '--flags USE_PLATFORM_ICU_ALTERNATIVES=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//url:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "url/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:buildflags__android_arm64
+cc_genrule {
+    name: "cronet_aml_url_buildflags__android_arm64",
+    cmd: "echo '--flags USE_PLATFORM_ICU_ALTERNATIVES=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//url:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "url/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:buildflags__android_x86
+cc_genrule {
+    name: "cronet_aml_url_buildflags__android_x86",
+    cmd: "echo '--flags USE_PLATFORM_ICU_ALTERNATIVES=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//url:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "url/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:buildflags__android_x86_64
+cc_genrule {
+    name: "cronet_aml_url_buildflags__android_x86_64",
+    cmd: "echo '--flags USE_PLATFORM_ICU_ALTERNATIVES=\"true\"' | " +
+         "$(location build/write_buildflag_header.py) --output " +
+         "$(out) " +
+         "--rulename " +
+         "//url:buildflags " +
+         "--gen-dir " +
+         ". " +
+         "--definitions " +
+         "/dev/stdin",
+    out: [
+        "url/buildflags.h",
+    ],
+    tool_files: [
+        "build/write_buildflag_header.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:url
+cc_library_static {
+    name: "cronet_aml_url_url",
+    srcs: [
+        "url/gurl.cc",
+        "url/origin.cc",
+        "url/scheme_host_port.cc",
+        "url/third_party/mozilla/url_parse.cc",
+        "url/url_canon.cc",
+        "url/url_canon_etc.cc",
+        "url/url_canon_filesystemurl.cc",
+        "url/url_canon_fileurl.cc",
+        "url/url_canon_host.cc",
+        "url/url_canon_internal.cc",
+        "url/url_canon_ip.cc",
+        "url/url_canon_mailtourl.cc",
+        "url/url_canon_path.cc",
+        "url/url_canon_pathurl.cc",
+        "url/url_canon_query.cc",
+        "url/url_canon_relative.cc",
+        "url/url_canon_stdstring.cc",
+        "url/url_canon_stdurl.cc",
+        "url/url_constants.cc",
+        "url/url_idna_icu_alternatives_android.cc",
+        "url/url_parse_file.cc",
+        "url/url_util.cc",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+    ],
+    static_libs: [
+        "cronet_aml_base_allocator_partition_allocator_partition_alloc",
+        "cronet_aml_base_base",
+        "cronet_aml_base_base_static",
+        "cronet_aml_base_third_party_double_conversion_double_conversion",
+        "cronet_aml_base_third_party_dynamic_annotations_dynamic_annotations",
+        "cronet_aml_third_party_boringssl_boringssl",
+        "cronet_aml_third_party_icu_icui18n",
+        "cronet_aml_third_party_icu_icuuc_private",
+        "cronet_aml_third_party_libevent_libevent",
+        "cronet_aml_third_party_modp_b64_modp_b64",
+    ],
+    defaults: [
+        "cronet_aml_defaults",
+    ],
+    cflags: [
+        "-DANDROID",
+        "-DANDROID_NDK_VERSION_ROLL=r23_1",
+        "-DCR_CLANG_REVISION=\"llvmorg-16-init-8697-g60809cd2-1\"",
+        "-DCR_LIBCXX_REVISION=47b31179d10646029c260702650a25d24f555acc",
+        "-DDCHECK_ALWAYS_ON=1",
+        "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+        "-DHAVE_SYS_UIO_H",
+        "-DIS_URL_IMPL",
+        "-D_DEBUG",
+        "-D_GNU_SOURCE",
+        "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
+        "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1",
+        "-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+    local_include_dirs: [
+        "./",
+        "buildtools/third_party/libc++/",
+        "buildtools/third_party/libc++/trunk/include",
+        "buildtools/third_party/libc++abi/trunk/include",
+        "third_party/abseil-cpp/",
+        "third_party/boringssl/src/include/",
+    ],
+    cpp_std: "c++20",
+    target: {
+        android_arm: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+                "cronet_aml_url_url_jni_headers__android_arm",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm",
+                "cronet_aml_base_logging_buildflags__android_arm",
+                "cronet_aml_build_chromeos_buildflags__android_arm",
+                "cronet_aml_url_buildflags__android_arm",
+                "cronet_aml_url_url_jni_headers__android_arm",
+            ],
+        },
+        android_arm64: {
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+                "cronet_aml_url_url_jni_headers__android_arm64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_arm64",
+                "cronet_aml_base_logging_buildflags__android_arm64",
+                "cronet_aml_build_chromeos_buildflags__android_arm64",
+                "cronet_aml_url_buildflags__android_arm64",
+                "cronet_aml_url_url_jni_headers__android_arm64",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+                "cronet_aml_url_url_jni_headers__android_x86",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86",
+                "cronet_aml_base_logging_buildflags__android_x86",
+                "cronet_aml_build_chromeos_buildflags__android_x86",
+                "cronet_aml_url_buildflags__android_x86",
+                "cronet_aml_url_url_jni_headers__android_x86",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-msse3",
+            ],
+            generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+                "cronet_aml_url_url_jni_headers__android_x86_64",
+            ],
+            export_generated_headers: [
+                "cronet_aml_base_debugging_buildflags__android_x86_64",
+                "cronet_aml_base_logging_buildflags__android_x86_64",
+                "cronet_aml_build_chromeos_buildflags__android_x86_64",
+                "cronet_aml_url_buildflags__android_x86_64",
+                "cronet_aml_url_url_jni_headers__android_x86_64",
+            ],
+        },
+    },
+}
+
+// GN: //url:url_jni_headers__android_arm
+cc_genrule {
+    name: "cronet_aml_url_url_jni_headers__android_arm",
+    srcs: [
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+        "url/android/java/src/org/chromium/url/Origin.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/url/url_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "IDNStringUtil_jni.h " +
+         "--output_name " +
+         "Origin_jni.h " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/IDNStringUtil.java) " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/Origin.java)",
+    out: [
+        "url/url_jni_headers/IDNStringUtil_jni.h",
+        "url/url_jni_headers/Origin_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:url_jni_headers__android_arm64
+cc_genrule {
+    name: "cronet_aml_url_url_jni_headers__android_arm64",
+    srcs: [
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+        "url/android/java/src/org/chromium/url/Origin.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/url/url_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "IDNStringUtil_jni.h " +
+         "--output_name " +
+         "Origin_jni.h " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/IDNStringUtil.java) " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/Origin.java)",
+    out: [
+        "url/url_jni_headers/IDNStringUtil_jni.h",
+        "url/url_jni_headers/Origin_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:url_jni_headers__android_x86
+cc_genrule {
+    name: "cronet_aml_url_url_jni_headers__android_x86",
+    srcs: [
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+        "url/android/java/src/org/chromium/url/Origin.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/url/url_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "IDNStringUtil_jni.h " +
+         "--output_name " +
+         "Origin_jni.h " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/IDNStringUtil.java) " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/Origin.java)",
+    out: [
+        "url/url_jni_headers/IDNStringUtil_jni.h",
+        "url/url_jni_headers/Origin_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
+// GN: //url:url_jni_headers__android_x86_64
+cc_genrule {
+    name: "cronet_aml_url_url_jni_headers__android_x86_64",
+    srcs: [
+        "url/android/java/src/org/chromium/url/IDNStringUtil.java",
+        "url/android/java/src/org/chromium/url/Origin.java",
+    ],
+    cmd: "$(location base/android/jni_generator/jni_generator.py) --ptr_type " +
+         "long " +
+         "--output_dir " +
+         "$(genDir)/url/url_jni_headers " +
+         "--includes " +
+         "base/android/jni_generator/jni_generator_helper.h " +
+         "--use_proxy_hash " +
+         "--output_name " +
+         "IDNStringUtil_jni.h " +
+         "--output_name " +
+         "Origin_jni.h " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/IDNStringUtil.java) " +
+         "--input_file " +
+         "$(location url/android/java/src/org/chromium/url/Origin.java)",
+    out: [
+        "url/url_jni_headers/IDNStringUtil_jni.h",
+        "url/url_jni_headers/Origin_jni.h",
+    ],
+    tool_files: [
+        "base/android/jni_generator/android_jar.classes",
+        "base/android/jni_generator/jni_generator.py",
+        "build/android/gyp/util/__init__.py",
+        "build/android/gyp/util/build_utils.py",
+        "build/gn_helpers.py",
+    ],
+    apex_available: [
+        "com.android.tethering",
+    ],
+}
+
diff --git a/tools/gn2bp/desc_arm.json b/tools/gn2bp/desc_arm.json
new file mode 100644
index 0000000..996b5f5
--- /dev/null
+++ b/tools/gn2bp/desc_arm.json
Binary files differ
diff --git a/tools/gn2bp/desc_arm64.json b/tools/gn2bp/desc_arm64.json
new file mode 100644
index 0000000..dd8e800
--- /dev/null
+++ b/tools/gn2bp/desc_arm64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x64.json b/tools/gn2bp/desc_x64.json
new file mode 100644
index 0000000..555f044
--- /dev/null
+++ b/tools/gn2bp/desc_x64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x86.json b/tools/gn2bp/desc_x86.json
new file mode 100644
index 0000000..11e4e95
--- /dev/null
+++ b/tools/gn2bp/desc_x86.json
Binary files differ
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
new file mode 100755
index 0000000..42cb494
--- /dev/null
+++ b/tools/gn2bp/gen_android_bp
@@ -0,0 +1,1485 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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.
+
+# This tool translates a collection of BUILD.gn files into a mostly equivalent
+# Android.bp file for the Android Soong build system. The input to the tool is a
+# JSON description of the GN build definition generated with the following
+# command:
+#
+#   gn desc out --format=json --all-toolchains "//*" > desc.json
+#
+# The tool is then given a list of GN labels for which to generate Android.bp
+# build rules. The dependencies for the GN labels are squashed to the generated
+# Android.bp target, except for actions which get their own genrule. Some
+# libraries are also mapped to their Android equivalents -- see |builtin_deps|.
+
+import argparse
+import collections
+import json
+import logging as log
+import operator
+import os
+import re
+import sys
+import copy
+from pathlib import Path
+
+import gn_utils
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# Default targets to translate to the blueprint file.
+default_targets = [
+    '//components/cronet/android:cronet',
+    '//components/cronet:cronet_package',
+]
+
+# Defines a custom init_rc argument to be applied to the corresponding output
+# blueprint target.
+target_initrc = {
+    # TODO: this can probably be removed.
+}
+
+target_host_supported = [
+    # TODO: remove if this is not useful for the cronet build.
+]
+
+# Proto target groups which will be made public.
+proto_groups = {
+    # TODO: remove if this is not used for the cronet build.
+}
+
+# All module names are prefixed with this string to avoid collisions.
+module_prefix = 'cronet_aml_'
+
+# Shared libraries which are directly translated to Android system equivalents.
+shared_library_allowlist = [
+    'android',
+    'android.hardware.atrace@1.0',
+    'android.hardware.health@2.0',
+    'android.hardware.health-V1-ndk',
+    'android.hardware.power.stats@1.0',
+    "android.hardware.power.stats-V1-cpp",
+    'base',
+    'binder',
+    'binder_ndk',
+    'cutils',
+    'hidlbase',
+    'hidltransport',
+    'hwbinder',
+    'incident',
+    'log',
+    'services',
+    'statssocket',
+    "tracingproxy",
+    'utils',
+]
+
+# Static libraries which are directly translated to Android system equivalents.
+static_library_allowlist = [
+    'statslog_perfetto',
+]
+
+# Include directories that will be removed from all targets.
+local_include_dirs_denylist = [
+]
+
+# Name of the module which settings such as compiler flags for all other
+# modules.
+defaults_module = module_prefix + 'defaults'
+
+# Location of the project in the Android source tree.
+tree_path = 'external/chromium_org'
+
+# Path for the protobuf sources in the standalone build.
+buildtools_protobuf_src = '//buildtools/protobuf/src'
+
+# Location of the protobuf src dir in the Android source tree.
+android_protobuf_src = 'external/protobuf/src'
+
+# put all args on a new line for better diffs.
+NEWLINE = ' " +\n         "'
+
+# Compiler flags which are passed through to the blueprint.
+cflag_allowlist = [
+  # needed for zlib:zlib
+  "-mpclmul",
+  # needed for zlib:zlib
+  "-mssse3",
+  # needed for zlib:zlib
+  "-msse3",
+  # needed for zlib:zlib
+  "-msse4.2",
+]
+
+# Additional arguments to apply to Android.bp rules.
+additional_args = {
+    # TODO: remove if not needed.
+    'cronet_aml_components_cronet_android_cronet': [
+        ('linker_scripts', {
+            'base/android/library_loader/anchor_functions.lds',
+        }),
+    ],
+    'cronet_aml_net_net': [
+        ('export_static_lib_headers', {
+            'cronet_aml_net_third_party_quiche_quiche',
+            'cronet_aml_crypto_crypto',
+        }),
+    ],
+}
+
+# Android equivalents for third-party libraries that the upstream project
+# depends on.
+builtin_deps = {
+    '//net/tools/root_store_tool:root_store_tool':
+        lambda x: None,
+}
+
+# Name of tethering apex module
+tethering_apex = "com.android.tethering"
+
+# ----------------------------------------------------------------------------
+# End of configuration.
+# ----------------------------------------------------------------------------
+
+
+class Error(Exception):
+  pass
+
+
+class ThrowingArgumentParser(argparse.ArgumentParser):
+
+  def __init__(self, context):
+    super(ThrowingArgumentParser, self).__init__()
+    self.context = context
+
+  def error(self, message):
+    raise Error('%s: %s' % (self.context, message))
+
+
+def write_blueprint_key_value(output, name, value, sort=True):
+  """Writes a Blueprint key-value pair to the output"""
+
+  if isinstance(value, bool):
+    if value:
+      output.append('    %s: true,' % name)
+    else:
+      output.append('    %s: false,' % name)
+    return
+  if not value:
+    return
+  if isinstance(value, set):
+    value = sorted(value)
+  if isinstance(value, list):
+    output.append('    %s: [' % name)
+    for item in sorted(value) if sort else value:
+      output.append('        "%s",' % item)
+    output.append('    ],')
+    return
+  if isinstance(value, Target):
+    value.to_string(output)
+    return
+  if isinstance(value, dict):
+    kv_output = []
+    for k, v in value.items():
+      write_blueprint_key_value(kv_output, k, v)
+
+    output.append('    %s: {' % name)
+    for line in kv_output:
+      output.append('    %s' % line)
+    output.append('    },')
+    return
+  output.append('    %s: "%s",' % (name, value))
+
+
+class Target(object):
+  """A target-scoped part of a module"""
+
+  def __init__(self, name):
+    self.name = name
+    self.srcs = set()
+    self.shared_libs = set()
+    self.static_libs = set()
+    self.whole_static_libs = set()
+    self.header_libs = set()
+    self.cflags = set()
+    self.dist = dict()
+    self.strip = dict()
+    self.stl = None
+    self.cppflags = set()
+    self.local_include_dirs = set()
+    self.export_system_include_dirs = set()
+    self.generated_headers = set()
+    self.export_generated_headers = set()
+
+  def to_string(self, output):
+    nested_out = []
+    self._output_field(nested_out, 'srcs')
+    self._output_field(nested_out, 'shared_libs')
+    self._output_field(nested_out, 'static_libs')
+    self._output_field(nested_out, 'whole_static_libs')
+    self._output_field(nested_out, 'header_libs')
+    self._output_field(nested_out, 'cflags')
+    self._output_field(nested_out, 'stl')
+    self._output_field(nested_out, 'dist')
+    self._output_field(nested_out, 'strip')
+    self._output_field(nested_out, 'cppflags')
+    self._output_field(nested_out, 'local_include_dirs')
+    self._output_field(nested_out, 'export_system_include_dirs')
+    self._output_field(nested_out, 'generated_headers')
+    self._output_field(nested_out, 'export_generated_headers')
+
+    if nested_out:
+      output.append('    %s: {' % self.name)
+      for line in nested_out:
+        output.append('    %s' % line)
+      output.append('    },')
+
+  def _output_field(self, output, name, sort=True):
+    value = getattr(self, name)
+    return write_blueprint_key_value(output, name, value, sort)
+
+
+class Module(object):
+  """A single module (e.g., cc_binary, cc_test) in a blueprint."""
+
+  def __init__(self, mod_type, name, gn_target):
+    self.type = mod_type
+    self.gn_target = gn_target
+    self.name = name
+    self.srcs = set()
+    self.comment = 'GN: ' + gn_target
+    self.shared_libs = set()
+    self.static_libs = set()
+    self.whole_static_libs = set()
+    self.runtime_libs = set()
+    self.tools = set()
+    self.cmd = None
+    self.host_supported = False
+    self.device_supported = True
+    self.vendor_available = False
+    self.init_rc = set()
+    self.out = set()
+    self.export_include_dirs = set()
+    self.generated_headers = set()
+    self.export_generated_headers = set()
+    self.export_static_lib_headers = set()
+    self.defaults = set()
+    self.cflags = set()
+    self.include_dirs = set()
+    self.local_include_dirs = set()
+    self.header_libs = set()
+    self.required = set()
+    self.tool_files = set()
+    # target contains a dict of Targets indexed by os_arch.
+    # example: { 'android_x86': Target('android_x86')
+    self.target = dict()
+    self.target['android'] = Target('android')
+    self.target['android_x86'] = Target('android_x86')
+    self.target['android_x86_64'] = Target('android_x86_64')
+    self.target['android_arm'] = Target('android_arm')
+    self.target['android_arm64'] = Target('android_arm64')
+    self.target['host'] = Target('host')
+    self.stl = None
+    self.cpp_std = None
+    self.dist = dict()
+    self.strip = dict()
+    self.data = set()
+    self.apex_available = set()
+    self.min_sdk_version = None
+    self.proto = dict()
+    self.linker_scripts = set()
+    # The genrule_XXX below are properties that must to be propagated back
+    # on the module(s) that depend on the genrule.
+    self.genrule_headers = set()
+    self.genrule_srcs = set()
+    self.genrule_shared_libs = set()
+    self.genrule_header_libs = set()
+    self.version_script = None
+    self.test_suites = set()
+    self.test_config = None
+    self.stubs = {}
+    self.cppflags = set()
+    self.rtti = False
+    # Name of the output. Used for setting .so file name for libcronet
+    self.stem = None
+    self.compile_multilib = None
+
+  def to_string(self, output):
+    if self.comment:
+      output.append('// %s' % self.comment)
+    output.append('%s {' % self.type)
+    self._output_field(output, 'name')
+    self._output_field(output, 'srcs')
+    self._output_field(output, 'shared_libs')
+    self._output_field(output, 'static_libs')
+    self._output_field(output, 'whole_static_libs')
+    self._output_field(output, 'runtime_libs')
+    self._output_field(output, 'tools')
+    self._output_field(output, 'cmd', sort=False)
+    if self.host_supported:
+      self._output_field(output, 'host_supported')
+    if not self.device_supported:
+      self._output_field(output, 'device_supported')
+    if self.vendor_available:
+      self._output_field(output, 'vendor_available')
+    self._output_field(output, 'init_rc')
+    self._output_field(output, 'out')
+    self._output_field(output, 'export_include_dirs')
+    self._output_field(output, 'generated_headers')
+    self._output_field(output, 'export_generated_headers')
+    self._output_field(output, 'export_static_lib_headers')
+    self._output_field(output, 'defaults')
+    self._output_field(output, 'cflags')
+    self._output_field(output, 'include_dirs')
+    self._output_field(output, 'local_include_dirs')
+    self._output_field(output, 'header_libs')
+    self._output_field(output, 'required')
+    self._output_field(output, 'dist')
+    self._output_field(output, 'strip')
+    self._output_field(output, 'tool_files')
+    self._output_field(output, 'data')
+    self._output_field(output, 'stl')
+    self._output_field(output, 'cpp_std')
+    self._output_field(output, 'apex_available')
+    self._output_field(output, 'min_sdk_version')
+    self._output_field(output, 'version_script')
+    self._output_field(output, 'test_suites')
+    self._output_field(output, 'test_config')
+    self._output_field(output, 'stubs')
+    self._output_field(output, 'proto')
+    self._output_field(output, 'linker_scripts')
+    self._output_field(output, 'cppflags')
+    self._output_field(output, 'stem')
+    self._output_field(output, 'compile_multilib')
+    if self.rtti:
+      self._output_field(output, 'rtti')
+
+    target_out = []
+    for arch, target in sorted(self.target.items()):
+      # _output_field calls getattr(self, arch).
+      setattr(self, arch, target)
+      self._output_field(target_out, arch)
+
+    if target_out:
+      output.append('    target: {')
+      for line in target_out:
+        output.append('    %s' % line)
+      output.append('    },')
+
+    output.append('}')
+    output.append('')
+
+  def add_android_static_lib(self, lib):
+    if self.type == 'cc_binary_host':
+      raise Exception('Adding Android static lib for host tool is unsupported')
+    elif self.host_supported:
+      self.target['android'].static_libs.add(lib)
+    else:
+      self.static_libs.add(lib)
+
+  def add_android_shared_lib(self, lib):
+    if self.type == 'cc_binary_host':
+      raise Exception('Adding Android shared lib for host tool is unsupported')
+    elif self.host_supported:
+      self.target['android'].shared_libs.add(lib)
+    else:
+      self.shared_libs.add(lib)
+
+  def _output_field(self, output, name, sort=True):
+    value = getattr(self, name)
+    return write_blueprint_key_value(output, name, value, sort)
+
+  def is_compiled(self):
+    return self.type not in ('cc_genrule', 'filegroup', 'java_genrule')
+
+  def is_genrule(self):
+    return self.type == "cc_genrule"
+
+  def has_input_files(self):
+    return len(self.srcs) > 0 or any([len(target.srcs) > 0 for target in self.target.values()])
+
+  def merge_attribute(self, key, source_module, allowed_archs, source_key = None):
+    """
+    Merges the value of the attribute `source_key` for the `dep_module` with
+    the value of the attribute `key` for this module. If the value of the
+    `source_key` is equal to None. Then `key` is used for both modules.
+
+    This merges the attribute for both non-arch and archs
+    specified in `allowed_archs`.
+    :param key: The attribute used for merging in the calling module. Also
+    used for `dep_module` if the `source_key` is None.
+    :param source_module: The module where data is propagated from.
+    :param allowed_archs: A list of archs to merge the attribute on.
+    :param source_key: if the attribute merged from the `dep_module`
+    is different from the `key`
+    """
+    if not source_key:
+      source_key = key
+    self.__dict__[key].update(source_module.__dict__[source_key])
+    for arch_name in source_module.target.keys():
+      if arch_name in allowed_archs:
+        self.target[arch_name].__dict__[key].update(
+            source_module.target[arch_name].__dict__[source_key])
+
+class Blueprint(object):
+  """In-memory representation of an Android.bp file."""
+
+  def __init__(self):
+    self.modules = {}
+
+  def add_module(self, module):
+    """Adds a new module to the blueprint, replacing any existing module
+        with the same name.
+
+        Args:
+            module: Module instance.
+        """
+    self.modules[module.name] = module
+
+  def to_string(self, output):
+    for m in sorted(self.modules.values(), key=lambda m: m.name):
+      m.to_string(output)
+
+
+def label_to_module_name(label):
+  """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
+  module = re.sub(r'^//:?', '', label)
+  module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
+
+  if not module.startswith(module_prefix):
+    return module_prefix + module
+  return module
+
+
+def is_supported_source_file(name):
+  """Returns True if |name| can appear in a 'srcs' list."""
+  return os.path.splitext(name)[1] in ['.c', '.cc', '.cpp', '.java', '.proto', '.S']
+
+
+def create_proto_modules(blueprint, gn, target):
+  """Generate genrules for a proto GN target.
+
+    GN actions are used to dynamically generate files during the build. The
+    Soong equivalent is a genrule. This function turns a specific kind of
+    genrule which turns .proto files into source and header files into a pair
+    equivalent genrules.
+
+    Args:
+        blueprint: Blueprint instance which is being generated.
+        target: gn_utils.Target object.
+
+    Returns:
+        The source_genrule module.
+    """
+  assert (target.type == 'proto_library')
+
+  protoc_gn_target_name = gn.get_target('//third_party/protobuf:protoc').name
+  protoc_module_name = label_to_module_name(protoc_gn_target_name)
+  tools = {protoc_module_name}
+  cpp_out_dir = '$(genDir)/%s/%s/' % (tree_path, target.proto_in_dir)
+  target_module_name = label_to_module_name(target.name)
+
+  # In GN builds the proto path is always relative to the output directory
+  # (out/tmp.xxx).
+  cmd = ['$(location %s)' % protoc_module_name]
+  cmd += ['--proto_path=%s/%s' % (tree_path, target.proto_in_dir)]
+
+  if buildtools_protobuf_src in target.proto_paths:
+    cmd += ['--proto_path=%s' % android_protobuf_src]
+
+  # We don't generate any targets for source_set proto modules because
+  # they will be inlined into other modules if required.
+  if target.proto_plugin == 'source_set':
+    return None
+
+  # Descriptor targets only generate a single target.
+  if target.proto_plugin == 'descriptor':
+    out = '{}.bin'.format(target_module_name)
+
+    cmd += ['--descriptor_set_out=$(out)']
+    cmd += ['$(in)']
+
+    descriptor_module = Module('cc_genrule', target_module_name, target.name)
+    descriptor_module.cmd = ' '.join(cmd)
+    descriptor_module.out = [out]
+    descriptor_module.tools = tools
+    blueprint.add_module(descriptor_module)
+
+    # Recursively extract the .proto files of all the dependencies and
+    # add them to srcs.
+    descriptor_module.srcs.update(
+        gn_utils.label_to_path(src) for src in target.sources)
+    for dep in target.transitive_proto_deps:
+      current_target = gn.get_target(dep)
+      descriptor_module.srcs.update(
+          gn_utils.label_to_path(src) for src in current_target.sources)
+
+    return descriptor_module
+
+  # We create two genrules for each proto target: one for the headers and
+  # another for the sources. This is because the module that depends on the
+  # generated files needs to declare two different types of dependencies --
+  # source files in 'srcs' and headers in 'generated_headers' -- and it's not
+  # valid to generate .h files from a source dependency and vice versa.
+  source_module_name = target_module_name + '_gen'
+  source_module = Module('cc_genrule', source_module_name, target.name)
+  blueprint.add_module(source_module)
+  source_module.srcs.update(
+      gn_utils.label_to_path(src) for src in target.sources)
+
+  header_module = Module('cc_genrule', source_module_name + '_headers',
+                         target.name)
+  blueprint.add_module(header_module)
+  header_module.srcs = set(source_module.srcs)
+
+  # TODO(primiano): at some point we should remove this. This was introduced
+  # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
+  # avoid doing multi-repo changes and allow old clients in the android tree
+  # to still do the old #include "perfetto/..." rather than
+  # #include "protos/perfetto/...".
+  header_module.export_include_dirs = {'.', 'protos'}
+  # Since the .cc file and .h get created by a different gerule target, they
+  # are not put in the same intermediate path, so local includes do not work
+  # without explictily exporting the include dir.
+  header_module.export_include_dirs.add(target.proto_in_dir)
+
+  # This function does not return header_module so setting apex_available attribute here.
+  header_module.apex_available.add(tethering_apex)
+
+  source_module.genrule_srcs.add(':' + source_module.name)
+  source_module.genrule_headers.add(header_module.name)
+
+  if target.proto_plugin == 'proto':
+    suffixes = ['pb']
+    source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
+    cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
+  elif target.proto_plugin == 'protozero':
+    suffixes = ['pbzero']
+    plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
+    tools.add(plugin.name)
+    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
+    cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
+  elif target.proto_plugin == 'cppgen':
+    suffixes = ['gen']
+    plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
+    tools.add(plugin.name)
+    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
+    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
+  elif target.proto_plugin == 'ipc':
+    suffixes = ['ipc']
+    plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
+    tools.add(plugin.name)
+    cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
+    cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
+  else:
+    raise Error('Unsupported proto plugin: %s' % target.proto_plugin)
+
+  cmd += ['$(in)']
+  source_module.cmd = ' '.join(cmd)
+  header_module.cmd = source_module.cmd
+  source_module.tools = tools
+  header_module.tools = tools
+
+  for sfx in suffixes:
+    source_module.out.update('%s/%s' %
+                             (tree_path, src.replace('.proto', '.%s.cc' % sfx))
+                             for src in source_module.srcs)
+    header_module.out.update('%s/%s' %
+                             (tree_path, src.replace('.proto', '.%s.h' % sfx))
+                             for src in header_module.srcs)
+  return source_module
+
+
+def create_proto_group_modules(blueprint, gn, module_name, target_names):
+  # TODO(lalitm): today, we're only adding a Java lite module because that's
+  # the only one used in practice. In the future, if we need other target types
+  # (e.g. C++, Java full etc.) add them here.
+  bp_module_name = label_to_module_name(module_name) + '_java_protos'
+  module = Module('java_library', bp_module_name, bp_module_name)
+  module.comment = f'''GN: [{', '.join(target_names)}]'''
+  module.proto = {'type': 'lite', 'canonical_path_from_root': False}
+
+  for name in target_names:
+    target = gn.get_target(name)
+    module.srcs.update(gn_utils.label_to_path(src) for src in target.sources)
+    for dep_label in target.transitive_proto_deps:
+      dep = gn.get_target(dep_label)
+      module.srcs.update(gn_utils.label_to_path(src) for src in dep.sources)
+
+  blueprint.add_module(module)
+
+def create_gcc_preprocess_modules(blueprint, target):
+  # gcc_preprocess.py internally execute host gcc which is not allowed in genrule.
+  # So, this function create multiple modules and realize equivalent processing
+  # TODO: Consider to support gcc_preprocess.py in different way
+  # It's not great to have genrule and cc_object in the dependency from java_library
+  assert (len(target.sources) == 1)
+  source = list(target.sources)[0]
+  assert (Path(source).suffix == '.template')
+  stem = Path(source).stem
+
+  bp_module_name = label_to_module_name(target.name)
+
+  # Rename .template to .cc since cc_object does not accept .template file as srcs
+  rename_module = Module('genrule', bp_module_name + '_rename', target.name)
+  rename_module.srcs.add(gn_utils.label_to_path(source))
+  rename_module.out.add(stem + '.cc')
+  rename_module.cmd = 'cp $(in) $(out)'
+  blueprint.add_module(rename_module)
+
+  # Preprocess template file and generates java file
+  preprocess_module = Module('cc_object', bp_module_name + '_preprocess', target.name)
+  # -E: stop after preprocessing.
+  # -P: disable line markers, i.e. '#line 309'
+  preprocess_module.cflags.update(['-E', '-P', '-DANDROID'])
+  preprocess_module.srcs.add(':' + rename_module.name)
+  defines = ['-D' + target.args[i+1] for i, arg in enumerate(target.args) if arg == '--define']
+  preprocess_module.cflags.update(defines)
+  # HACK: Specifying compile_multilib to build cc_object only once.
+  # Without this, soong complain to genrule that depends on cc_object when built for 64bit target.
+  # It seems this is because cc object is a module with per-architecture variants and genrule is a
+  # module with default variant. For 64bit target, cc_object is built multiple times for 32/64bit
+  # modes and genrule doesn't know which one to depend on.
+  preprocess_module.compile_multilib = 32
+  blueprint.add_module(preprocess_module)
+
+  # Generates srcjar using soong_zip
+  module = Module('genrule', bp_module_name, target.name)
+  module.srcs.add(':' + preprocess_module.name)
+  module.out.add(stem + '.srcjar')
+  module.cmd = NEWLINE.join([
+    f'cp $(in) $(genDir)/{stem}.java &&',
+    f'$(location soong_zip) -o $(out) -srcjar -f $(genDir)/{stem}.java'
+  ])
+  module.tools.add('soong_zip')
+  blueprint.add_module(module)
+  return module
+
+
+class BaseActionSanitizer():
+  def __init__(self, target):
+    # Just to be on the safe side, create a deep-copy.
+    self.target = copy.deepcopy(target)
+    self.target.args = self._normalize_args()
+
+  def get_name(self):
+    return label_to_module_name(self.target.name)
+
+  def _normalize_args(self):
+    # Convert ['--param=value'] to ['--param', 'value'] for consistency.
+    # Escape quotations.
+    normalized_args = []
+    for arg in self.target.args:
+      arg = arg.replace('"', r'\"')
+      if arg.startswith('-'):
+        normalized_args.extend(arg.split('='))
+      else:
+        normalized_args.append(arg)
+    return normalized_args
+
+  # There are three types of args:
+  # - flags (--flag)
+  # - value args (--arg value)
+  # - list args (--arg value1 --arg value2)
+  # value args have exactly one arg value pair and list args have one or more arg value pairs.
+  # Note that the set of list args contains the set of value args.
+  # This is because list and value args are identical when the list args has only one arg value pair
+  # Some functions provide special implementations for each type, while others
+  # work on all of them.
+  def _has_arg(self, arg):
+    return arg in self.target.args
+
+  def _get_arg_indices(self, target_arg):
+    return [i for i, arg in enumerate(self.target.args) if arg == target_arg]
+
+  # Whether an arg value pair appears once or more times
+  def _is_list_arg(self, arg):
+    indices = self._get_arg_indices(arg)
+    return len(indices) > 0 and all([not self.target.args[i + 1].startswith('--') for i in indices])
+
+  def _update_list_arg(self, arg, func, throw_if_absent = True):
+    if self._should_fail_silently(arg, throw_if_absent):
+      return
+    assert(self._is_list_arg(arg))
+    indices = self._get_arg_indices(arg)
+    for i in indices:
+      self._set_arg_at(i + 1, func(self.target.args[i + 1]))
+
+  # Whether an arg value pair appears exactly once
+  def _is_value_arg(self, arg):
+    return operator.countOf(self.target.args, arg) == 1 and self._is_list_arg(arg)
+
+  def _get_value_arg(self, arg):
+    assert(self._is_value_arg(arg))
+    i = self.target.args.index(arg)
+    return self.target.args[i + 1]
+
+  # used to check whether a function call should cause an error when an arg is
+  # missing.
+  def _should_fail_silently(self, arg, throw_if_absent):
+    return not throw_if_absent and not self._has_arg(arg)
+
+  def _set_value_arg(self, arg, value, throw_if_absent = True):
+    if self._should_fail_silently(arg, throw_if_absent):
+      return
+    assert(self._is_value_arg(arg))
+    i = self.target.args.index(arg)
+    self.target.args[i + 1] = value
+
+  def _update_value_arg(self, arg, func, throw_if_absent = True):
+    if self._should_fail_silently(arg, throw_if_absent):
+      return
+    self._set_value_arg(arg, func(self._get_value_arg(arg)))
+
+  def _set_arg_at(self, position, value):
+    self.target.args[position] = value
+
+  def _delete_value_arg(self, arg, throw_if_absent = True):
+    if self._should_fail_silently(arg, throw_if_absent):
+      return
+    assert(self._is_value_arg(arg))
+    i = self.target.args.index(arg)
+    self.target.args.pop(i)
+    self.target.args.pop(i)
+
+  def _append_arg(self, arg, value):
+    self.target.args.append(arg)
+    self.target.args.append(value)
+
+  def _sanitize_filepath_with_location_tag(self, arg):
+    if arg.startswith('../../'):
+      arg = self._sanitize_filepath(arg)
+      arg = self._add_location_tag(arg)
+    return arg
+
+  # wrap filename in location tag.
+  def _add_location_tag(self, filename):
+    return '$(location %s)' % filename
+
+  # applies common directory transformation that *should* be universally applicable.
+  # TODO: verify if it actually *is* universally applicable.
+  def _sanitize_filepath(self, filepath):
+    # Careful, order matters!
+    # delete all leading ../
+    filepath = re.sub('^(\.\./)+', '', filepath)
+    filepath = re.sub('^gen/jni_headers', '$(genDir)', filepath)
+    filepath = re.sub('^gen', '$(genDir)', filepath)
+    return filepath
+
+  # Iterate through all the args and apply function
+  def _update_all_args(self, func):
+    self.target.args = [func(arg) for arg in self.target.args]
+
+  def get_cmd(self):
+    arg_string = NEWLINE.join(self.target.args)
+    cmd = '$(location %s) %s' % (
+    gn_utils.label_to_path(self.target.script), arg_string)
+
+    if self.use_response_file:
+      # Pipe response file contents into script
+      cmd = 'echo \'%s\' |%s%s' % (self.target.response_file_contents, NEWLINE, cmd)
+    return cmd
+
+  def get_outputs(self):
+    return self.target.outputs
+
+  def get_srcs(self):
+    # gn treats inputs and sources for actions equally.
+    # soong only supports source files inside srcs, non-source files are added as
+    # tool_files dependency.
+    files = self.target.sources.union(self.target.inputs)
+    return {gn_utils.label_to_path(file) for file in files if is_supported_source_file(file)}
+
+  def get_tool_files(self):
+    # gn treats inputs and sources for actions equally.
+    # soong only supports source files inside srcs, non-source files are added as
+    # tool_files dependency.
+    files = self.target.sources.union(self.target.inputs)
+    tool_files = {gn_utils.label_to_path(file)
+                  for file in files if not is_supported_source_file(file)}
+    tool_files.add(gn_utils.label_to_path(self.target.script))
+    return tool_files
+
+  def _sanitize_args(self):
+    # Handle passing parameters via response file by piping them into the script
+    # and reading them from /dev/stdin.
+
+    self.use_response_file = gn_utils.RESPONSE_FILE in self.target.args
+    if self.use_response_file:
+      # Replace {{response_file_contents}} with /dev/stdin
+      self.target.args = ['/dev/stdin' if it == gn_utils.RESPONSE_FILE else it
+                          for it in self.target.args]
+
+  def _sanitize_outputs(self):
+    pass
+
+  def _sanitize_inputs(self):
+    pass
+
+  def sanitize(self):
+    self._sanitize_args()
+    self._sanitize_outputs()
+    self._sanitize_inputs()
+
+  # Whether this target generates header files
+  def is_header_generated(self):
+    return any(os.path.splitext(it)[1] == '.h' for it in self.target.outputs)
+
+class WriteBuildDateHeaderSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._set_arg_at(0, '$(out)')
+    super()._sanitize_args()
+
+class WriteBuildFlagHeaderSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._set_value_arg('--gen-dir', '.')
+    self._set_value_arg('--output', '$(out)')
+    super()._sanitize_args()
+
+class JniGeneratorSanitizer(BaseActionSanitizer):
+  def _add_location_tag_to_filepath(self, arg):
+    if not arg.endswith('.class'):
+      # --input_file supports both .class specifiers or source files as arguments.
+      # Only source files need to be wrapped inside a $(location <label>) tag.
+      arg = self._add_location_tag(arg)
+    return arg
+
+  def _sanitize_args(self):
+    self._update_value_arg('--jar_file', self._sanitize_filepath, False)
+    self._update_value_arg('--jar_file', self._add_location_tag, False)
+    if self._has_arg('--jar_file'):
+      self._append_arg('--javap', '$$(find out/.path -name javap)')
+    self._update_value_arg('--output_dir', self._sanitize_filepath)
+    self._update_value_arg('--includes', self._sanitize_filepath, False)
+    self._delete_value_arg('--prev_output_dir', False)
+    self._update_list_arg('--input_file', self._sanitize_filepath)
+    self._update_list_arg('--input_file', self._add_location_tag_to_filepath)
+    super()._sanitize_args()
+
+  def _sanitize_outputs(self):
+    # fix target.output directory to match #include statements.
+    self.target.outputs = {re.sub('^jni_headers/', '', out) for out in self.target.outputs}
+    super()._sanitize_outputs()
+
+  def get_tool_files(self):
+    tool_files = super().get_tool_files()
+    # android_jar.classes should be part of the tools as it list implicit classes
+    # for the script to generate JNI headers.
+    tool_files.add("base/android/jni_generator/android_jar.classes")
+    return tool_files
+
+class JniRegistrationGeneratorSanitizer(BaseActionSanitizer):
+  def _sanitize_inputs(self):
+    self.target.inputs = [file for file in self.target.inputs if not file.startswith('//out/')]
+
+  def _sanitize_args(self):
+    self._update_value_arg('--depfile', self._sanitize_filepath)
+    self._update_value_arg('--srcjar-path', self._sanitize_filepath)
+    self._update_value_arg('--header-path', self._sanitize_filepath)
+    self._set_value_arg('--sources-files', '$(genDir)/java.sources')
+    # update_jni_registration_module removes them from the srcs of the module
+    # It might be better to remove sources by '--sources-exclusions'
+    self._delete_value_arg('--sources-exclusions')
+    super()._sanitize_args()
+
+  def get_cmd(self):
+    # jni_registration_generator.py doesn't work with python2
+    cmd = "python3 " + super().get_cmd()
+    # Path in the original sources file does not work in genrule.
+    # So creating sources file in cmd based on the srcs of this target.
+    # Adding ../$(current_dir)/ to the head because jni_registration_generator.py uses the files
+    # whose path startswith(..)
+    commands = ["current_dir=`basename \\\`pwd\\\``;",
+                "for f in $(in);",
+                "do",
+                "echo \\\"../$$current_dir/$$f\\\" >> $(genDir)/java.sources;",
+                "done;",
+                cmd]
+
+    # .h file jni_registration_generator.py generates has #define with directory name.
+    # With the genrule env that contains "." which is invalid. So replace that at the end of cmd.
+    commands.append(";sed -i -e 's/OUT_SOONG_.TEMP_SBOX_.*_OUT/GEN/g' ")
+    commands.append("$(genDir)/components/cronet/android/cronet_jni_registration.h")
+    return NEWLINE.join(commands)
+
+class JavaJniRegistrationGeneratorSanitizer(JniRegistrationGeneratorSanitizer):
+  def get_name(self):
+    return label_to_module_name(self.target.name) + "__java"
+
+  def _sanitize_outputs(self):
+    self.target.outputs = [out for out in self.target.outputs if
+                           out.endswith(".srcjar")]
+    super()._sanitize_outputs()
+
+class VersionSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._set_value_arg('-o', '$(out)')
+    # args for the version.py contain file path without leading --arg key. So apply sanitize
+    # function for all the args.
+    self._update_all_args(self._sanitize_filepath_with_location_tag)
+    self._set_value_arg('-e', "'%s'" % self._get_value_arg('-e'))
+    super()._sanitize_args()
+
+  def get_tool_files(self):
+    tool_files = super().get_tool_files()
+    # android_chrome_version.py is not specified in anywhere but version.py imports this file
+    tool_files.add('build/util/android_chrome_version.py')
+    return tool_files
+
+class JavaCppEnumSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._update_all_args(self._sanitize_filepath_with_location_tag)
+    self._set_value_arg('--srcjar', '$(out)')
+    super()._sanitize_args()
+
+class MakeDafsaSanitizer(BaseActionSanitizer):
+  def is_header_generated(self):
+    # This script generates .cc files but they are #included by other sources
+    # (e.g. registry_controlled_domain.cc)
+    return True
+
+class JavaCppFeatureSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._update_all_args(self._sanitize_filepath_with_location_tag)
+    self._set_value_arg('--srcjar', '$(out)')
+    super()._sanitize_args()
+
+class JavaCppStringSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._update_all_args(self._sanitize_filepath_with_location_tag)
+    self._set_value_arg('--srcjar', '$(out)')
+    super()._sanitize_args()
+
+class WriteNativeLibrariesJavaSanitizer(BaseActionSanitizer):
+  def _sanitize_args(self):
+    self._set_value_arg('--output', '$(out)')
+    super()._sanitize_args()
+
+def get_action_sanitizer(target, type):
+  if target.script == "//build/write_buildflag_header.py":
+    return WriteBuildFlagHeaderSanitizer(target)
+  elif target.script == "//build/write_build_date_header.py":
+    return WriteBuildDateHeaderSanitizer(target)
+  elif target.script == '//base/android/jni_generator/jni_generator.py':
+    return JniGeneratorSanitizer(target)
+  elif target.script == '//base/android/jni_generator/jni_registration_generator.py':
+    if type == 'java_genrule':
+      return JavaJniRegistrationGeneratorSanitizer(target)
+    else:
+      return JniRegistrationGeneratorSanitizer(target)
+  elif target.script == "//build/util/version.py":
+    return VersionSanitizer(target)
+  elif target.script == "//build/android/gyp/java_cpp_enum.py":
+    return JavaCppEnumSanitizer(target)
+  elif target.script == "//net/tools/dafsa/make_dafsa.py":
+    return MakeDafsaSanitizer(target)
+  elif target.script == '//build/android/gyp/java_cpp_features.py':
+    return JavaCppFeatureSanitizer(target)
+  elif target.script == '//build/android/gyp/java_cpp_strings.py':
+    return JavaCppStringSanitizer(target)
+  elif target.script == '//build/android/gyp/write_native_libraries_java.py':
+    return WriteNativeLibrariesJavaSanitizer(target)
+  else:
+    # TODO: throw exception here once all script hacks have been converted.
+    return BaseActionSanitizer(target)
+
+def create_action_foreach_modules(blueprint, target):
+  """ The following assumes that rebase_path exists in the args.
+  The args of an action_foreach contains hints about which output files are generated
+  by which source files.
+  This is copied directly from the args
+  "gen/net/base/registry_controlled_domains/{{source_name_part}}-reversed-inc.cc"
+  So each source file will generate an output whose name is the {source_name-reversed-inc.cc}
+  """
+  new_args = []
+  for i, src in enumerate(sorted(target.sources)):
+    # don't add script arg for the first source -- create_action_module
+    # already does this.
+    if i != 0:
+      new_args.append('&& python3 $(location %s)' %
+                   gn_utils.label_to_path(target.script))
+    for arg in target.args:
+      if '{{source}}' in arg:
+        new_args.append('$(location %s)' % (gn_utils.label_to_path(src)))
+      elif '{{source_name_part}}' in arg:
+        source_name_part = src.split("/")[-1] # Get the file name only
+        source_name_part = source_name_part.split(".")[0] # Remove the extension (Ex: .cc)
+        file_name = arg.replace('{{source_name_part}}', source_name_part).split("/")[-1]
+        # file_name represent the output file name. But we need the whole path
+        # This can be found from target.outputs.
+        for out in target.outputs:
+          if out.endswith(file_name):
+            new_args.append('$(location %s)' % out)
+      else:
+        new_args.append(arg)
+
+  target.args = new_args
+  return create_action_module(blueprint, target, 'cc_genrule')
+
+def create_action_module(blueprint, target, type):
+  sanitizer = get_action_sanitizer(target, type)
+  sanitizer.sanitize()
+
+  module = Module(type, sanitizer.get_name(), target.name)
+  module.cmd = sanitizer.get_cmd()
+  module.out = sanitizer.get_outputs()
+  if sanitizer.is_header_generated():
+    module.genrule_headers.add(module.name)
+  module.srcs = sanitizer.get_srcs()
+  module.tool_files = sanitizer.get_tool_files()
+
+  blueprint.add_module(module)
+  return module
+
+
+
+def _get_cflags(cflags, defines):
+  cflags = {flag for flag in cflags if flag in cflag_allowlist}
+  # Consider proper allowlist or denylist if needed
+  cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in defines)
+  return cflags
+
+def set_module_flags(module, cflags, defines):
+  module.cflags.update(_get_cflags(cflags, defines))
+  # TODO: implement proper cflag parsing.
+  for flag in cflags:
+    if '-std=' in flag:
+      module.cpp_std = flag[len('-std='):]
+    if '-fexceptions' in flag:
+      module.cppflags.add('-fexceptions')
+
+def add_genrule_per_arch(module, dep_module, type):
+  module.generated_headers.update(dep_module.genrule_headers)
+  # If the module is a static library, export all the generated headers.
+  if type == 'cc_library_static':
+    module.export_generated_headers.update(dep_module.genrule_headers)
+  module.srcs.update(dep_module.genrule_srcs)
+  module.shared_libs.update(dep_module.genrule_shared_libs)
+  module.header_libs.update(dep_module.genrule_header_libs)
+
+def set_module_include_dirs(module, cflags, include_dirs):
+  for flag in cflags:
+    if '-isystem' in flag:
+      module.local_include_dirs.add(flag[len('-isystem../../'):])
+
+  # Adding local_include_dirs is necessary due to source_sets / filegroups
+  # which do not properly propagate include directories.
+  # Filter any directory inside //out as a) this directory does not exist for
+  # aosp / soong builds and b) the include directory should already be
+  # configured via library dependency.
+  module.local_include_dirs.update([gn_utils.label_to_path(d)
+                                 for d in include_dirs
+                                 if not re.match('^//out/.*', d)])
+
+def create_modules_from_target(blueprint, gn, gn_target_name):
+  """Generate module(s) for a given GN target.
+
+    Given a GN target name, generate one or more corresponding modules into a
+    blueprint. The only case when this generates >1 module is proto libraries.
+
+    Args:
+        blueprint: Blueprint instance which is being generated.
+        gn: gn_utils.GnParser object.
+        gn_target_name: GN target for module generation.
+    """
+  bp_module_name = label_to_module_name(gn_target_name)
+  if bp_module_name in blueprint.modules:
+    return blueprint.modules[bp_module_name]
+  target = gn.get_target(gn_target_name)
+  log.info('create modules for %s (%s)', target.name, target.type)
+
+  if target.type == 'executable':
+    if target.testonly:
+      module_type = 'cc_test'
+    else:
+      # Can be used for both host and device targets.
+      module_type = 'cc_binary'
+    module = Module(module_type, bp_module_name, gn_target_name)
+  elif target.type == 'static_library':
+    module = Module('cc_library_static', bp_module_name, gn_target_name)
+  elif target.type == 'shared_library':
+    module = Module('cc_library_shared', bp_module_name, gn_target_name)
+  elif target.type == 'source_set':
+    module = Module('cc_object', bp_module_name, gn_target_name)
+  elif target.type == 'group':
+    # "group" targets are resolved recursively by gn_utils.get_target().
+    # There's nothing we need to do at this level for them.
+    return None
+  elif target.type == 'proto_library':
+    module = create_proto_modules(blueprint, gn, target)
+    if module is None:
+      return None
+  elif target.type == 'action':
+    module = create_action_module(blueprint, target, 'cc_genrule')
+  elif target.type == 'action_foreach':
+    module = create_action_foreach_modules(blueprint, target)
+  elif target.type == 'copy':
+    # TODO: careful now! copy targets are not supported yet, but this will stop
+    # traversing the dependency tree. For //base:base, this is not a big
+    # problem as libicu contains the only copy target which happens to be a
+    # leaf node.
+    return None
+  elif target.type == 'java_group':
+    # Java targets are handled outside of create_modules_from_target.
+    return None
+  else:
+    raise Error('Unknown target %s (%s)' % (target.name, target.type))
+
+  blueprint.add_module(module)
+  module.init_rc = target_initrc.get(target.name, [])
+  module.srcs.update(
+      gn_utils.label_to_path(src)
+      for src in target.sources
+      if is_supported_source_file(src) and not src.startswith("//out/test"))
+
+  # Add arch-specific properties
+  for arch_name, arch in target.arch.items():
+    module.target[arch_name].srcs.update(
+      gn_utils.label_to_path(src)
+      for src in arch.sources
+      if is_supported_source_file(src) and not src.startswith("//out/test"))
+
+  module.rtti = target.rtti
+
+  if target.type in gn_utils.LINKER_UNIT_TYPES:
+    set_module_flags(module, target.cflags, target.defines)
+    set_module_include_dirs(module, target.cflags, target.include_dirs)
+    # TODO: set_module_xxx is confusing, apply similar function to module and target in better way.
+    for arch_name, arch in target.arch.items():
+      set_module_flags(module.target[arch_name], arch.cflags, arch.defines)
+      # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions).
+      # Flags which does not start with '-' could not be in the cflags so enabling MTE by
+      # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for
+      # building //base/allocator/partition_allocator:partition_alloc for arm64.
+      if '+mte' in arch.cflags and arch_name == 'android_arm64':
+        module.target[arch_name].cflags.add('-march=armv8-a+memtag')
+      set_module_include_dirs(module.target[arch_name], arch.cflags, arch.include_dirs)
+
+  module.host_supported = target.host_supported()
+  module.device_supported = target.device_supported()
+
+  if module.is_genrule():
+    module.apex_available.add(tethering_apex)
+
+  if module.is_compiled():
+    # Don't try to inject library/source dependencies into genrules or
+    # filegroups because they are not compiled in the traditional sense.
+    module.defaults = [defaults_module]
+    for lib in target.libs:
+      # Generally library names should be mangled as 'libXXX', unless they
+      # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
+      # libraries (e.g. "android.hardware.power.stats-V1-cpp")
+      android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
+        else 'lib' + lib
+      if lib in shared_library_allowlist:
+        module.add_android_shared_lib(android_lib)
+      if lib in static_library_allowlist:
+        module.add_android_static_lib(android_lib)
+
+    # Remove prohibited include directories
+    module.local_include_dirs = [d for d in module.local_include_dirs
+                                 if d not in local_include_dirs_denylist]
+
+  # If the module is a static library, export all the generated headers.
+  if module.type == 'cc_library_static':
+    module.export_generated_headers = module.generated_headers
+
+  if module.name == 'cronet_aml_components_cronet_android_cronet':
+    if target.output_name is None:
+      raise Error('Failed to get output_name for libcronet name')
+    # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so)
+    # So setting the output name based on the output_name from the desc.json
+    module.stem = 'lib' + target.output_name
+
+  # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
+  # Currently, only one module is generated from target even target has multiple toolchains.
+  # And module is generated based on the first visited target.
+  # Sort deps before iteration to make result deterministic.
+  all_deps = sorted(target.deps | target.source_set_deps | target.transitive_proto_deps)
+  for dep_name in all_deps:
+    # |builtin_deps| override GN deps with Android-specific ones. See the
+    # config in the top of this file.
+    if dep_name in builtin_deps:
+      builtin_deps[dep_name](module)
+      continue
+
+    dep_module = create_modules_from_target(blueprint, gn, dep_name)
+
+    if dep_module is None:
+      continue
+    # TODO: Proper dependency check for genrule.
+    # Currently, only propagating genrule dependencies.
+    # Also, currently, all the dependencies are propagated upwards.
+    # in gn, public_deps should be propagated but deps should not.
+    # Not sure this information is available in the desc.json.
+    # Following rule works for adding android_runtime_jni_headers to base:base.
+    # If this doesn't work for other target, hardcoding for specific target
+    # might be better.
+    if module.is_genrule() and dep_module.is_genrule():
+      module.genrule_headers.add(dep_module.name)
+      module.genrule_headers.update(dep_module.genrule_headers)
+
+    # For filegroups, and genrule, recurse but don't apply the
+    # deps.
+    if not module.is_compiled() or module.is_genrule():
+      continue
+
+    if dep_module.type == 'cc_library_shared':
+      module.shared_libs.add(dep_module.name)
+    elif dep_module.type == 'cc_library_static':
+      module.static_libs.add(dep_module.name)
+    elif dep_module.type == 'cc_object':
+      module.merge_attribute('generated_headers', dep_module, target.arch.keys())
+      if module.type != 'cc_object':
+        if dep_module.has_input_files():
+          # Only add it as part of srcs if the dep_module has input files otherwise
+          # this would throw an error.
+          module.srcs.add(":" + dep_module.name)
+        module.merge_attribute('export_generated_headers', dep_module,
+                         target.arch.keys(), 'generated_headers')
+    elif dep_module.type == 'cc_genrule':
+      module.merge_attribute('generated_headers', dep_module, [], 'genrule_headers')
+      module.merge_attribute('srcs', dep_module, [], 'genrule_srcs')
+      module.merge_attribute('shared_libs', dep_module, [], 'genrule_shared_libs')
+      module.merge_attribute('header_libs', dep_module, [], 'genrule_header_libs')
+      if module.type not in ["cc_object"]:
+        module.merge_attribute('export_generated_headers', dep_module, [],
+                         'genrule_headers')
+    elif dep_module.type == 'cc_binary':
+      continue  # Ignore executables deps (used by cmdline integration tests).
+    else:
+      raise Error('Unknown dep %s (%s) for target %s' %
+                  (dep_module.name, dep_module.type, module.name))
+
+  for arch_name, arch in target.arch.items():
+    for dep_name in arch.deps:
+      dep_module = create_modules_from_target(blueprint, gn, dep_name)
+      # Arch-specific dependencies currently only include cc_library_static.
+      # Revisit this approach once we need to support more target types.
+      if dep_module.type == 'cc_library_static':
+        module.target[arch_name].static_libs.add(dep_module.name)
+      elif dep_module.type == 'cc_genrule':
+        if dep_module.name.endswith(arch_name):
+          module.target[arch_name].generated_headers.update(dep_module.genrule_headers)
+          module.target[arch_name].srcs.update(dep_module.genrule_srcs)
+          module.target[arch_name].shared_libs.update(dep_module.genrule_shared_libs)
+          module.target[arch_name].header_libs.update(dep_module.genrule_header_libs)
+          if module.type not in ["cc_object"]:
+            module.target[arch_name].export_generated_headers.update(
+              dep_module.genrule_headers)
+      else:
+        raise Error('Unsupported arch-specific dependency %s of target %s with type %s' %
+                    (dep_module.name, target.name, dep_module.type))
+    for dep_name in arch.source_set_deps:
+      dep_module = create_modules_from_target(blueprint, gn, dep_name)
+      if dep_module.type == 'cc_object':
+        if module.type != 'cc_object':
+          # We only want to bubble up cc_objects for modules that are not cc_objects
+          # otherwise they'd be recompiled and that would cause multiple symbol redefinitions.
+          if dep_module.has_input_files():
+            # Only add it as part of srcs if the dep_module has input files otherwise
+            # this would throw an error.
+            module.target[arch_name].srcs.add(":" + dep_module.name)
+      else:
+        raise Error('Unsupported arch-specific dependency %s of target %s with type %s' %
+                    (dep_module.name, target.name, dep_module.type))
+  return module
+
+def create_java_module(blueprint, gn):
+  bp_module_name = module_prefix + 'java'
+  module = Module('java_library', bp_module_name, '//gn:java')
+  module.srcs.update([gn_utils.label_to_path(source) for source in gn.java_sources])
+  for dep in gn.java_actions:
+    target = gn.get_target(dep)
+    if target.script == '//build/android/gyp/gcc_preprocess.py':
+      module.srcs.add(':' + create_gcc_preprocess_modules(blueprint, target).name)
+    else:
+      module.srcs.add(':' + create_action_module(blueprint, target, 'java_genrule').name)
+  blueprint.add_module(module)
+
+def update_jni_registration_module(module, gn):
+  # TODO: deny list is in the arg of jni_registration_generator.py. Should not be hardcoded
+  deny_list = [
+    '//base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
+    '//base/android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java',
+    '//base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java',
+    '//base/android/java/src/org/chromium/base/SysUtils.java']
+
+  # TODO: java_sources might not contain all the required java files
+  module.srcs.update([gn_utils.label_to_path(source)
+                      for source in gn.java_sources
+                      if source.endswith('.java') and source not in deny_list])
+
+def create_blueprint_for_targets(gn, targets):
+  """Generate a blueprint for a list of GN targets."""
+  blueprint = Blueprint()
+
+  # Default settings used by all modules.
+  defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
+  defaults.cflags = [
+      '-DGOOGLE_PROTOBUF_NO_RTTI',
+      '-Wno-error=return-type',
+      '-Wno-non-virtual-dtor',
+      '-Wno-macro-redefined',
+      '-Wno-missing-field-initializers',
+      '-Wno-sign-compare',
+      '-Wno-sign-promo',
+      '-Wno-unused-parameter',
+      '-Wno-null-pointer-subtraction', # Needed to libevent
+      '-Wno-deprecated-non-prototype', # needed for zlib
+      '-fvisibility=hidden',
+      '-Wno-ambiguous-reversed-operator', # needed for icui18n
+      '-Wno-unreachable-code-loop-increment', # needed for icui18n
+      '-O2',
+      '-fPIC',
+  ]
+  # Chromium builds do not add a dependency for headers found inside the
+  # sysroot, so they are added globally via defaults.
+  defaults.target['android'].header_libs = [
+      'media_ndk_headers',
+      'jni_headers',
+  ]
+  defaults.target['host'].cflags = [
+      # -DANDROID is added by default but target.defines contain -DANDROID if
+      # it's required.  So adding -UANDROID to cancel default -DANDROID if it's
+      # not specified.
+      # Note: -DANDROID is not consistently applied across the chromium code
+      # base, so it is removed unconditionally for host targets.
+      '-UANDROID',
+  ]
+  defaults.stl = 'none'
+  defaults.min_sdk_version = 29
+  defaults.apex_available.add(tethering_apex)
+  blueprint.add_module(defaults)
+
+  for target in targets:
+    create_modules_from_target(blueprint, gn, target)
+
+  create_java_module(blueprint, gn)
+  for module in blueprint.modules.values():
+    if 'cronet_jni_registration' in module.name:
+      update_jni_registration_module(module, gn)
+
+  # Merge in additional hardcoded arguments.
+  for module in blueprint.modules.values():
+    for key, add_val in additional_args.get(module.name, []):
+      curr = getattr(module, key)
+      if add_val and isinstance(add_val, set) and isinstance(curr, set):
+        curr.update(add_val)
+      elif isinstance(add_val, str) and (not curr or isinstance(curr, str)):
+        setattr(module, key, add_val)
+      elif isinstance(add_val, bool) and (not curr or isinstance(curr, bool)):
+        setattr(module, key, add_val)
+      elif isinstance(add_val, dict) and isinstance(curr, dict):
+        curr.update(add_val)
+      elif isinstance(add_val, dict) and isinstance(curr, Target):
+        curr.__dict__.update(add_val)
+      else:
+        raise Error('Unimplemented type %r of additional_args: %r' %
+                    (type(add_val), key))
+
+  return blueprint
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description='Generate Android.bp from a GN description.')
+  parser.add_argument(
+      '--desc',
+      help='GN description (e.g., gn desc out --format=json --all-toolchains "//*".' +
+           'You can specify multiple --desc options for different target_cpu',
+      required=True,
+      action='append'
+  )
+  parser.add_argument(
+      '--extras',
+      help='Extra targets to include at the end of the Blueprint file',
+      default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
+  )
+  parser.add_argument(
+      '--output',
+      help='Blueprint file to create',
+      default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
+  )
+  parser.add_argument(
+      '-v',
+      '--verbose',
+      help='Print debug logs.',
+      action='store_true',
+  )
+  parser.add_argument(
+      'targets',
+      nargs=argparse.REMAINDER,
+      help='Targets to include in the blueprint (e.g., "//:perfetto_tests")'
+  )
+  args = parser.parse_args()
+
+  if args.verbose:
+    log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s', level=log.DEBUG)
+
+  targets = args.targets or default_targets
+  gn = gn_utils.GnParser()
+  for desc_file in args.desc:
+    with open(desc_file) as f:
+      desc = json.load(f)
+    for target in targets:
+      gn.parse_gn_desc(desc, target)
+  blueprint = create_blueprint_for_targets(gn, targets)
+  project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+  tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
+
+  # Add any proto groups to the blueprint.
+  for l_name, t_names in proto_groups.items():
+    create_proto_group_modules(blueprint, gn, l_name, t_names)
+
+  output = [
+      """// Copyright (C) 2022 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.
+//
+// This file is automatically generated by %s. Do not edit.
+""" % (tool_name)
+  ]
+  blueprint.to_string(output)
+  if os.path.exists(args.extras):
+    with open(args.extras, 'r') as r:
+      for line in r:
+        output.append(line.rstrip("\n\r"))
+
+  out_files = []
+
+  # Generate the Android.bp file.
+  out_files.append(args.output + '.swp')
+  with open(out_files[-1], 'w') as f:
+    f.write('\n'.join(output))
+    # Text files should have a trailing EOL.
+    f.write('\n')
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/gn2bp/gn_utils.py b/tools/gn2bp/gn_utils.py
new file mode 100644
index 0000000..130f8ff
--- /dev/null
+++ b/tools/gn2bp/gn_utils.py
@@ -0,0 +1,497 @@
+# Copyright (C) 2022 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.
+
+# A collection of utilities for extracting build rule information from GN
+# projects.
+
+from __future__ import print_function
+import collections
+import errno
+import filecmp
+import json
+import logging as log
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+BUILDFLAGS_TARGET = '//gn:gen_buildflags'
+GEN_VERSION_TARGET = '//src/base:version_gen_h'
+LINKER_UNIT_TYPES = ('executable', 'shared_library', 'static_library', 'source_set')
+JAVA_BANNED_SCRIPTS = [
+    "//build/android/gyp/turbine.py",
+    "//build/android/gyp/compile_java.py",
+    "//build/android/gyp/filter_zip.py",
+    "//build/android/gyp/dex.py",
+    "//build/android/gyp/write_build_config.py",
+    "//build/android/gyp/create_r_java.py",
+    "//build/android/gyp/ijar.py",
+    "//build/android/gyp/create_r_java.py",
+    "//build/android/gyp/bytecode_processor.py",
+    "//build/android/gyp/prepare_resources.py",
+    "//build/android/gyp/aar.py",
+    "//build/android/gyp/zip.py",
+]
+# TODO(primiano): investigate these, they require further componentization.
+ODR_VIOLATION_IGNORE_TARGETS = {
+    '//test/cts:perfetto_cts_deps',
+    '//:perfetto_integrationtests',
+}
+ARCH_REGEX = r'(android_x86_64|android_x86|android_arm|android_arm64|host)'
+DEX_REGEX = '.*__dex__%s$' % ARCH_REGEX
+COMPILE_JAVA_REGEX = '.*__compile_java__%s$' % ARCH_REGEX
+RESPONSE_FILE = '{{response_file_name}}'
+
+def repo_root():
+  """Returns an absolute path to the repository root."""
+  return os.path.join(
+      os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
+
+
+def label_to_path(label):
+  """Turn a GN output label (e.g., //some_dir/file.cc) into a path."""
+  assert label.startswith('//')
+  return label[2:] or "./"
+
+
+def label_without_toolchain(label):
+  """Strips the toolchain from a GN label.
+
+    Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain:
+    gcc_like_host) without the parenthesised toolchain part.
+    """
+  return label.split('(')[0]
+
+
+def label_to_target_name_with_path(label):
+  """
+  Turn a GN label into a target name involving the full path.
+  e.g., //src/perfetto:tests -> src_perfetto_tests
+  """
+  name = re.sub(r'^//:?', '', label)
+  name = re.sub(r'[^a-zA-Z0-9_]', '_', name)
+  return name
+
+def _is_java_source(src):
+  return os.path.splitext(src)[1] == '.java' and not src.startswith("//out/test/gen/")
+
+def is_java_action(script, outputs):
+  return (script != "" and script not in JAVA_BANNED_SCRIPTS) and any(
+      [file.endswith(".srcjar") or file.endswith(".java")
+       for file in outputs])
+
+class GnParser(object):
+  """A parser with some cleverness for GN json desc files
+
+    The main goals of this parser are:
+    1) Deal with the fact that other build systems don't have an equivalent
+       notion to GN's source_set. Conversely to Bazel's and Soong's filegroups,
+       GN source_sets expect that dependencies, cflags and other source_set
+       properties propagate up to the linker unit (static_library, executable or
+       shared_library). This parser simulates the same behavior: when a
+       source_set is encountered, some of its variables (cflags and such) are
+       copied up to the dependent targets. This is to allow gen_xxx to create
+       one filegroup for each source_set and then squash all the other flags
+       onto the linker unit.
+    2) Detect and special-case protobuf targets, figuring out the protoc-plugin
+       being used.
+    """
+
+  class Target(object):
+    """Reperesents A GN target.
+
+        Maked properties are propagated up the dependency chain when a
+        source_set dependency is encountered.
+        """
+    class Arch():
+      """Architecture-dependent properties
+        """
+      def __init__(self):
+        self.sources = set()
+        self.cflags = set()
+        self.defines = set()
+        self.include_dirs = set()
+        self.deps = set()
+        self.transitive_static_libs_deps = set()
+        self.source_set_deps = set()
+
+
+    def __init__(self, name, type):
+      self.name = name  # e.g. //src/ipc:ipc
+
+      VALID_TYPES = ('static_library', 'shared_library', 'executable', 'group',
+                     'action', 'source_set', 'proto_library', 'copy', 'action_foreach')
+      assert (type in VALID_TYPES)
+      self.type = type
+      self.testonly = False
+      self.toolchain = None
+
+      # These are valid only for type == proto_library.
+      # This is typically: 'proto', 'protozero', 'ipc'.
+      self.proto_plugin = None
+      self.proto_paths = set()
+      self.proto_exports = set()
+      self.proto_in_dir = ""
+
+      self.sources = set()
+      # TODO(primiano): consider whether the public section should be part of
+      # bubbled-up sources.
+      self.public_headers = set()  # 'public'
+
+      # These are valid only for type == 'action'
+      self.inputs = set()
+      self.outputs = set()
+      self.script = None
+      self.args = []
+      self.response_file_contents = None
+
+      # These variables are propagated up when encountering a dependency
+      # on a source_set target.
+      self.cflags = set()
+      self.defines = set()
+      self.deps = set()
+      self.libs = set()
+      self.include_dirs = set()
+      self.ldflags = set()
+      self.source_set_deps = set()  # Transitive set of source_set deps.
+      self.proto_deps = set()
+      self.transitive_proto_deps = set()
+      self.rtti = False
+
+      # TODO: come up with a better way to only run this once.
+      # is_finalized tracks whether finalize() was called on this target.
+      self.is_finalized = False
+      self.arch = dict()
+
+      # This is used to get the name/version of libcronet
+      self.output_name = None
+
+    def host_supported(self):
+      return 'host' in self.arch
+
+    def device_supported(self):
+      return any([name.startswith('android') for name in self.arch.keys()])
+
+    def __lt__(self, other):
+      if isinstance(other, self.__class__):
+        return self.name < other.name
+      raise TypeError(
+          '\'<\' not supported between instances of \'%s\' and \'%s\'' %
+          (type(self).__name__, type(other).__name__))
+
+    def __repr__(self):
+      return json.dumps({
+          k: (list(sorted(v)) if isinstance(v, set) else v)
+          for (k, v) in self.__dict__.items()
+      },
+                        indent=4,
+                        sort_keys=True)
+
+    def update(self, other, arch):
+      for key in ('cflags', 'defines', 'deps', 'include_dirs', 'ldflags',
+                  'source_set_deps', 'proto_deps', 'transitive_proto_deps',
+                  'libs', 'proto_paths'):
+        self.__dict__[key].update(other.__dict__.get(key, []))
+
+      for key_in_arch in ('cflags', 'defines', 'include_dirs', 'source_set_deps'):
+        self.arch[arch].__dict__[key_in_arch].update(
+          other.arch[arch].__dict__.get(key_in_arch, []))
+
+    def finalize(self):
+      """Move common properties out of arch-dependent subobjects to Target object.
+
+        TODO: find a better name for this function.
+        """
+      if self.is_finalized:
+        return
+      self.is_finalized = True
+
+      # Target contains the intersection of arch-dependent properties
+      self.sources = set.intersection(*[arch.sources for arch in self.arch.values()])
+      self.cflags = set.intersection(*[arch.cflags for arch in self.arch.values()])
+      self.defines = set.intersection(*[arch.defines for arch in self.arch.values()])
+      self.include_dirs = set.intersection(*[arch.include_dirs for arch in self.arch.values()])
+      self.deps.update(set.intersection(*[arch.deps for arch in self.arch.values()]))
+      self.source_set_deps.update(set.intersection(*[arch.source_set_deps for arch in self.arch.values()]))
+
+      # Deduplicate arch-dependent properties
+      for arch in self.arch.keys():
+        self.arch[arch].sources -= self.sources
+        self.arch[arch].cflags -= self.cflags
+        self.arch[arch].defines -= self.defines
+        self.arch[arch].include_dirs -= self.include_dirs
+        self.arch[arch].deps -= self.deps
+        self.arch[arch].source_set_deps -= self.source_set_deps
+
+
+  def __init__(self):
+    self.all_targets = {}
+    self.linker_units = {}  # Executables, shared or static libraries.
+    self.source_sets = {}
+    self.actions = {}
+    self.proto_libs = {}
+    self.java_sources = set()
+    self.java_actions = set()
+
+  def _get_response_file_contents(self, action_desc):
+    # response_file_contents are formatted as:
+    # ['--flags', '--flag=true && false'] and need to be formatted as:
+    # '--flags --flag=\"true && false\"'
+    flags = action_desc.get('response_file_contents', [])
+    formatted_flags = []
+    for flag in flags:
+      if '=' in flag:
+        key, val = flag.split('=')
+        formatted_flags.append('%s=\\"%s\\"' % (key, val))
+      else:
+        formatted_flags.append(flag)
+
+    return ' '.join(formatted_flags)
+
+  def _is_java_target(self, target):
+    # Per https://chromium.googlesource.com/chromium/src/build/+/HEAD/android/docs/java_toolchain.md
+    # java target names must end in "_java".
+    # TODO: There are some other possible variations we might need to support.
+    return target.type == 'group' and re.match('.*_java$', target.name)
+
+  def _get_arch(self, toolchain):
+    if toolchain == '//build/toolchain/android:android_clang_x86':
+      return 'android_x86'
+    elif toolchain == '//build/toolchain/android:android_clang_x64':
+      return 'android_x86_64'
+    elif toolchain == '//build/toolchain/android:android_clang_arm':
+      return 'android_arm'
+    elif toolchain == '//build/toolchain/android:android_clang_arm64':
+      return 'android_arm64'
+    else:
+      return 'host'
+
+  def get_target(self, gn_target_name):
+    """Returns a Target object from the fully qualified GN target name.
+
+      get_target() requires that parse_gn_desc() has already been called.
+      """
+    # Run this every time as parse_gn_desc can be called at any time.
+    for target in self.all_targets.values():
+      target.finalize()
+
+    return self.all_targets[label_without_toolchain(gn_target_name)]
+
+  def parse_gn_desc(self, gn_desc, gn_target_name):
+    """Parses a gn desc tree and resolves all target dependencies.
+
+        It bubbles up variables from source_set dependencies as described in the
+        class-level comments.
+        """
+    # Use name without toolchain for targets to support targets built for
+    # multiple archs.
+    target_name = label_without_toolchain(gn_target_name)
+    desc = gn_desc[gn_target_name]
+    type_ = desc['type']
+    arch = self._get_arch(desc['toolchain'])
+
+    # Action modules can differ depending on the target architecture, yet
+    # genrule's do not allow to overload cmd per target OS / arch.  Create a
+    # separate action for every architecture.
+    # Cover both action and action_foreach
+    if type_.startswith('action') and \
+        not is_java_action(desc.get("script", ""), desc.get("outputs", [])):
+      # Don't meddle with the java actions name
+      target_name += '__' + arch
+
+    target = self.all_targets.get(target_name)
+    if target is None:
+      target = GnParser.Target(target_name, type_)
+      self.all_targets[target_name] = target
+
+    if arch not in target.arch:
+      target.arch[arch] = GnParser.Target.Arch()
+    else:
+      return target  # Target already processed.
+
+    target.testonly = desc.get('testonly', False)
+
+    proto_target_type, proto_desc = self.get_proto_target_type(gn_desc, gn_target_name)
+    if proto_target_type is not None:
+      self.proto_libs[target.name] = target
+      target.type = 'proto_library'
+      target.proto_plugin = proto_target_type
+      target.proto_paths.update(self.get_proto_paths(proto_desc))
+      target.proto_exports.update(self.get_proto_exports(proto_desc))
+      target.proto_in_dir = self.get_proto_in_dir(proto_desc)
+      for gn_proto_deps_name in proto_desc.get('deps', []):
+        dep = self.parse_gn_desc(gn_desc, gn_proto_deps_name)
+        target.deps.add(dep.name)
+      target.arch[arch].sources.update(proto_desc.get('sources', []))
+      assert (all(x.endswith('.proto') for x in target.arch[arch].sources))
+    elif target.type == 'source_set':
+      self.source_sets[gn_target_name] = target
+      target.arch[arch].sources.update(desc.get('sources', []))
+    elif target.type in LINKER_UNIT_TYPES:
+      self.linker_units[gn_target_name] = target
+      target.arch[arch].sources.update(desc.get('sources', []))
+    elif desc.get("script", "") in JAVA_BANNED_SCRIPTS or self._is_java_target(target):
+      # java_group identifies the group target generated by the android_library
+      # or java_library template. A java_group must not be added as a dependency, but sources are collected
+      log.debug('Found java target %s', target.name)
+      if target.type == "action":
+        # Convert java actions into java_group and keep the inputs for collection.
+        target.inputs.update(desc.get('inputs', []))
+      target.type = 'java_group'
+    elif target.type in ['action', 'action_foreach']:
+      self.actions[gn_target_name] = target
+      target.inputs.update(desc.get('inputs', []))
+      target.arch[arch].sources.update(desc.get('sources', []))
+      outs = [re.sub('^//out/.+?/gen/', '', x) for x in desc['outputs']]
+      target.outputs.update(outs)
+      target.script = desc['script']
+      target.args = desc['args']
+      target.response_file_contents = self._get_response_file_contents(desc)
+    elif target.type == 'copy':
+      # TODO: copy rules are not currently implemented.
+      self.actions[gn_target_name] = target
+
+    # Default for 'public' is //* - all headers in 'sources' are public.
+    # TODO(primiano): if a 'public' section is specified (even if empty), then
+    # the rest of 'sources' is considered inaccessible by gn. Consider
+    # emulating that, so that generated build files don't end up with overly
+    # accessible headers.
+    public_headers = [x for x in desc.get('public', []) if x != '*']
+    target.public_headers.update(public_headers)
+
+    target.arch[arch].cflags.update(desc.get('cflags', []) + desc.get('cflags_cc', []))
+    target.libs.update(desc.get('libs', []))
+    target.ldflags.update(desc.get('ldflags', []))
+    target.arch[arch].defines.update(desc.get('defines', []))
+    target.arch[arch].include_dirs.update(desc.get('include_dirs', []))
+    target.output_name = desc.get('output_name', None)
+    if "-frtti" in target.arch[arch].cflags:
+      target.rtti = True
+
+    # Recurse in dependencies.
+    for gn_dep_name in desc.get('deps', []):
+      dep = self.parse_gn_desc(gn_desc, gn_dep_name)
+      if dep.type == 'proto_library':
+        target.proto_deps.add(dep.name)
+        target.transitive_proto_deps.add(dep.name)
+        target.proto_paths.update(dep.proto_paths)
+        target.transitive_proto_deps.update(dep.transitive_proto_deps)
+      elif dep.type == 'source_set':
+        target.arch[arch].source_set_deps.add(dep.name)
+        target.arch[arch].source_set_deps.update(dep.arch[arch].source_set_deps)
+      elif dep.type == 'group':
+        target.update(dep, arch)  # Bubble up groups's cflags/ldflags etc.
+      elif dep.type in ['action', 'action_foreach', 'copy']:
+        if proto_target_type is None:
+          target.arch[arch].deps.add(dep.name)
+      elif dep.type in LINKER_UNIT_TYPES:
+        target.arch[arch].deps.add(dep.name)
+      elif dep.type == 'java_group':
+        # Explicitly break dependency chain when a java_group is added.
+        # Java sources are collected and eventually compiled as one large
+        # java_library.
+        pass
+
+      # Source set bubble up transitive source sets but can't be combined with this
+      # if they are combined then source sets will bubble up static libraries
+      # while we only want to have source sets bubble up only source sets.
+      if dep.type == 'static_library':
+        # Bubble up static_libs. Necessary, since soong does not propagate
+        # static_libs up the build tree.
+        target.arch[arch].transitive_static_libs_deps.add(dep.name)
+
+      if arch in dep.arch:
+        target.arch[arch].transitive_static_libs_deps.update(
+            dep.arch[arch].transitive_static_libs_deps)
+        target.arch[arch].deps.update(target.arch[arch].transitive_static_libs_deps)
+
+      # Collect java sources. Java sources are kept inside the __compile_java target.
+      # This target can be used for both host and target compilation; only add
+      # the sources if they are destined for the target (i.e. they are a
+      # dependency of the __dex target)
+      # Note: this skips prebuilt java dependencies. These will have to be
+      # added manually when building the jar.
+      if re.match(DEX_REGEX, target.name):
+        if re.match(COMPILE_JAVA_REGEX, dep.name):
+          log.debug('Adding java sources for %s', dep.name)
+          java_srcs = [src for src in dep.inputs if _is_java_source(src)]
+          self.java_sources.update(java_srcs)
+      if dep.type in ["action"] and target.type == "java_group":
+        # //base:base_java_aidl generates srcjar from .aidl files. But java_library in soong can
+        # directly have .aidl files in srcs. So adding .aidl files to the java_sources.
+        # TODO: Find a better way/place to do this.
+        if dep.name == '//base:base_java_aidl':
+          self.java_sources.update(dep.arch[arch].sources)
+        else:
+          self.java_actions.add(dep.name)
+    return target
+
+  def get_proto_exports(self, proto_desc):
+    # exports in metadata will be available for source_set targets.
+    metadata = proto_desc.get('metadata', {})
+    return metadata.get('exports', [])
+
+  def get_proto_paths(self, proto_desc):
+    # import_dirs in metadata will be available for source_set targets.
+    metadata = proto_desc.get('metadata', {})
+    return metadata.get('import_dirs', [])
+
+
+  def get_proto_in_dir(self, proto_desc):
+    args = proto_desc.get('args')
+    return re.sub('^\.\./\.\./', '', args[args.index('--proto-in-dir') + 1])
+
+  def get_proto_target_type(self, gn_desc, gn_target_name):
+    """ Checks if the target is a proto library and return the plugin.
+
+        Returns:
+            (None, None): if the target is not a proto library.
+            (plugin, proto_desc) where |plugin| is 'proto' in the default (lite)
+            case or 'protozero' or 'ipc' or 'descriptor'; |proto_desc| is the GN
+            json desc of the target with the .proto sources (_gen target for
+            non-descriptor types or the target itself for descriptor type).
+        """
+    parts = gn_target_name.split('(', 1)
+    name = parts[0]
+    toolchain = '(' + parts[1] if len(parts) > 1 else ''
+
+    # Descriptor targets don't have a _gen target; instead we look for the
+    # characteristic flag in the args of the target itself.
+    desc = gn_desc.get(gn_target_name)
+    if '--descriptor_set_out' in desc.get('args', []):
+      return 'descriptor', desc
+
+    # Source set proto targets have a non-empty proto_library_sources in the
+    # metadata of the description.
+    metadata = desc.get('metadata', {})
+    if 'proto_library_sources' in metadata:
+      return 'source_set', desc
+
+    # In all other cases, we want to look at the _gen target as that has the
+    # important information.
+    gen_desc = gn_desc.get('%s_gen%s' % (name, toolchain))
+    if gen_desc is None or gen_desc['type'] != 'action':
+      return None, None
+    if gen_desc['script'] != '//tools/protoc_wrapper/protoc_wrapper.py':
+      return None, None
+    plugin = 'proto'
+    args = gen_desc.get('args', [])
+    for arg in (arg for arg in args if arg.startswith('--plugin=')):
+      # |arg| at this point looks like:
+      #  --plugin=protoc-gen-plugin=gcc_like_host/protozero_plugin
+      # or
+      #  --plugin=protoc-gen-plugin=protozero_plugin
+      plugin = arg.split('=')[-1].split('/')[-1].replace('_plugin', '')
+    return plugin, gen_desc
diff --git a/tools/gn2bp/update_results.sh b/tools/gn2bp/update_results.sh
new file mode 100755
index 0000000..a464604
--- /dev/null
+++ b/tools/gn2bp/update_results.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# This script is expected to run after gen_android_bp is modified.
+#
+#   ./update_result.sh
+#
+# TARGETS contains targets which are supported by gen_android_bp and
+# this script generates Android.bp.swp from TARGETS.
+# This makes it easy to realize unintended impact/degression on
+# previously supported targets.
+
+set -eux
+
+BASEDIR=$(dirname "$0")
+$BASEDIR/gen_android_bp --desc $BASEDIR/desc_x64.json --desc $BASEDIR/desc_x86.json \
+--desc $BASEDIR/desc_arm.json --desc $BASEDIR/desc_arm64.json --out $BASEDIR/Android.bp
diff --git a/tools/testdata/java/android/net/IpSecTransform.java b/tools/testdata/java/android/net/IpSecTransform.java
new file mode 100644
index 0000000..0140bc5
--- /dev/null
+++ b/tools/testdata/java/android/net/IpSecTransform.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 android.net;
+
+/**
+ * Test class with a name matching a public API in a secondary (framework-connectivity-t) stubs jar.
+ */
+public class IpSecTransform {
+}
diff --git a/tools/testdata/java/test/unsupportedappusage/OtherUnsupportedUsageClass.java b/tools/testdata/java/test/unsupportedappusage/OtherUnsupportedUsageClass.java
new file mode 100644
index 0000000..9d3ae2e0
--- /dev/null
+++ b/tools/testdata/java/test/unsupportedappusage/OtherUnsupportedUsageClass.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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 test.unsupportedappusage;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
+public class OtherUnsupportedUsageClass {
+    // The annotation is just for completeness, what matters is the unsupportedappusage.txt file
+    @UnsupportedAppUsage
+    public void testSecondMethod() {}
+}
diff --git a/tools/testdata/java/test/unsupportedappusage/TestUnsupportedAppUsageClass.java b/tools/testdata/java/test/unsupportedappusage/TestUnsupportedAppUsageClass.java
index 9d32296..460c91b 100644
--- a/tools/testdata/java/test/unsupportedappusage/TestUnsupportedAppUsageClass.java
+++ b/tools/testdata/java/test/unsupportedappusage/TestUnsupportedAppUsageClass.java
@@ -16,6 +16,11 @@
 
 package test.unsupportedappusage;
 
+
+import android.compat.annotation.UnsupportedAppUsage;
+
 public class TestUnsupportedAppUsageClass {
+    // The annotation is just for completeness, what matters is the unsupportedappusage.txt file
+    @UnsupportedAppUsage
     public void testMethod() {}
 }
diff --git a/tools/testdata/test-other-unsupportedappusage.txt b/tools/testdata/test-other-unsupportedappusage.txt
new file mode 100644
index 0000000..b7d74a4
--- /dev/null
+++ b/tools/testdata/test-other-unsupportedappusage.txt
@@ -0,0 +1 @@
+Ltest/unsupportedappusage/OtherUnsupportedUsageClass;->testSecondMethod()V
\ No newline at end of file