Merge "clat: just always set mark unconditionally"
diff --git a/Cronet/Android.bp b/Cronet/Android.bp
deleted file mode 100644
index 3ce88ef..0000000
--- a/Cronet/Android.bp
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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
deleted file mode 100644
index c6471ed..0000000
--- a/Cronet/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.net.http"
- android:versionCode="11"
- android:versionName="R-initial">
-</manifest>
diff --git a/Cronet/TEST_MAPPING b/Cronet/TEST_MAPPING
deleted file mode 100644
index b1f3088..0000000
--- a/Cronet/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsCronetTestCases"
- }
- ]
-}
diff --git a/Cronet/apex/Android.bp b/Cronet/apex/Android.bp
deleted file mode 100644
index 180dafb..0000000
--- a/Cronet/apex/Android.bp
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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 = "CronetApexDefaultsEnabled"
-// 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
deleted file mode 100644
index 650badc..0000000
--- a/Cronet/apex/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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
deleted file mode 100644
index 38aebe0..0000000
--- a/Cronet/apex/com.android.cronet.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/Cronet/apex/com.android.cronet.pem b/Cronet/apex/com.android.cronet.pem
deleted file mode 100644
index 438653f..0000000
--- a/Cronet/apex/com.android.cronet.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------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
deleted file mode 100644
index a63d761..0000000
--- a/Cronet/apex/com.android.cronet.pk8
+++ /dev/null
Binary files differ
diff --git a/Cronet/apex/com.android.cronet.x509.pem b/Cronet/apex/com.android.cronet.x509.pem
deleted file mode 100644
index c9cd874..0000000
--- a/Cronet/apex/com.android.cronet.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------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
deleted file mode 100644
index 0301e9f..0000000
--- a/Cronet/apex/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.cronet",
- "version": 1
-}
diff --git a/Cronet/api/current.txt b/Cronet/api/current.txt
deleted file mode 100644
index 21779bc..0000000
--- a/Cronet/api/current.txt
+++ /dev/null
@@ -1,175 +0,0 @@
-// 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
deleted file mode 100644
index 0e2f25b..0000000
--- a/Cronet/api/lint-baseline.txt
+++ /dev/null
@@ -1,263 +0,0 @@
-// 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
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/module-lib-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/module-lib-removed.txt b/Cronet/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/removed.txt b/Cronet/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/system-current.txt b/Cronet/api/system-current.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/system-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/system-removed.txt b/Cronet/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/test-current.txt b/Cronet/api/test-current.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/test-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/api/test-removed.txt b/Cronet/api/test-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/Cronet/api/test-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Cronet/jarjar-rules.txt b/Cronet/jarjar-rules.txt
deleted file mode 100644
index 76a799e..0000000
--- a/Cronet/jarjar-rules.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-rule androidx.** com.android.net.http.@0
-rule android.support.** com.android.net.http.@0
-
diff --git a/Cronet/prebuilt/VERSION b/Cronet/prebuilt/VERSION
deleted file mode 100644
index c16ab94..0000000
--- a/Cronet/prebuilt/VERSION
+++ /dev/null
@@ -1,4 +0,0 @@
-MAJOR=80
-MINOR=0
-BUILD=3986
-PATCH=0
diff --git a/Cronet/prebuilt/api_version.txt b/Cronet/prebuilt/api_version.txt
deleted file mode 100644
index 48082f7..0000000
--- a/Cronet/prebuilt/api_version.txt
+++ /dev/null
@@ -1 +0,0 @@
-12
diff --git a/Cronet/prebuilt/cronet_api-src.jar b/Cronet/prebuilt/cronet_api-src.jar
deleted file mode 100644
index 924b877..0000000
--- a/Cronet/prebuilt/cronet_api-src.jar
+++ /dev/null
Binary files differ
diff --git a/Cronet/prebuilt/cronet_api.jar b/Cronet/prebuilt/cronet_api.jar
deleted file mode 100644
index 977b28d..0000000
--- a/Cronet/prebuilt/cronet_api.jar
+++ /dev/null
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_common_java.jar b/Cronet/prebuilt/cronet_impl_common_java.jar
deleted file mode 100644
index fa71bf3..0000000
--- a/Cronet/prebuilt/cronet_impl_common_java.jar
+++ /dev/null
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_native_java.jar b/Cronet/prebuilt/cronet_impl_native_java.jar
deleted file mode 100644
index 4cdd6f3..0000000
--- a/Cronet/prebuilt/cronet_impl_native_java.jar
+++ /dev/null
Binary files differ
diff --git a/Cronet/prebuilt/cronet_impl_platform_java.jar b/Cronet/prebuilt/cronet_impl_platform_java.jar
deleted file mode 100644
index 6d6042f..0000000
--- a/Cronet/prebuilt/cronet_impl_platform_java.jar
+++ /dev/null
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
deleted file mode 100644
index 7f2540a..0000000
--- a/Cronet/prebuilt/libs/arm64-v8a/libcronet.107.0.5284.2.so
+++ /dev/null
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
deleted file mode 100644
index 115429a..0000000
--- a/Cronet/prebuilt/libs/armeabi-v7a/libcronet.107.0.5284.2.so
+++ /dev/null
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
deleted file mode 100644
index 27923f7..0000000
--- a/Cronet/prebuilt/libs/x86/libcronet.107.0.5284.2.so
+++ /dev/null
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
deleted file mode 100644
index 803e5cd..0000000
--- a/Cronet/prebuilt/libs/x86_64/libcronet.107.0.5284.2.so
+++ /dev/null
Binary files differ
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
index d10c68c..2c28b8d 100644
--- a/Cronet/tests/cts/Android.bp
+++ b/Cronet/tests/cts/Android.bp
@@ -18,10 +18,36 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// cronet_test_java_defaults can be used to specify a java_defaults target that
+// either enables or disables Cronet tests. This is used to disable Cronet
+// tests on tm-mainline-prod where the required APIs are not present.
+cronet_test_java_defaults = "CronetTestJavaDefaultsEnabled"
+// This is a placeholder comment to avoid merge conflicts
+// as cronet_test_java_defaults may have different values
+// depending on the branch
+
+java_defaults {
+ name: "CronetTestJavaDefaultsEnabled",
+ enabled: true,
+}
+
+java_defaults {
+ name: "CronetTestJavaDefaultsDisabled",
+ enabled: false,
+}
+
+java_defaults {
+ name: "CronetTestJavaDefaults",
+ defaults: [cronet_test_java_defaults],
+}
+
android_test {
- name: "CtsCronetTestCases",
+ name: "CtsNetHttpTestCases",
compile_multilib: "both", // Include both the 32 and 64 bit versions
- defaults: ["cts_defaults"],
+ defaults: [
+ "CronetTestJavaDefaults",
+ "cts_defaults",
+ ],
sdk_version: "test_current",
srcs: [
"src/**/*.java",
@@ -33,13 +59,14 @@
"ctstestrunner-axt",
"ctstestserver",
"junit",
+ "hamcrest-library",
],
libs: [
"android.test.runner",
"android.test.base",
"android.test.mock",
"androidx.annotation_annotation",
- "framework-cronet",
+ "framework-tethering",
"org.apache.http.legacy",
],
diff --git a/Cronet/tests/cts/AndroidManifest.xml b/Cronet/tests/cts/AndroidManifest.xml
index 5a92dea..eaa24aa 100644
--- a/Cronet/tests/cts/AndroidManifest.xml
+++ b/Cronet/tests/cts/AndroidManifest.xml
@@ -25,7 +25,6 @@
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
- <uses-library android:name="framework-cronet"/>
</application>
<instrumentation
diff --git a/Cronet/tests/cts/AndroidTest.xml b/Cronet/tests/cts/AndroidTest.xml
index d2422f1..e0421fd 100644
--- a/Cronet/tests/cts/AndroidTest.xml
+++ b/Cronet/tests/cts/AndroidTest.xml
@@ -23,14 +23,14 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsCronetTestCases.apk" />
+ <option name="test-file-name" value="CtsNetHttpTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.http.cts" />
<option name="runtime-hint" value="10s" />
</test>
- <!-- Only run CtsCronetTestcasess in MTS if the Tethering Mainline module is installed. -->
+ <!-- Only run CtsNetHttpTestCases in MTS if the Tethering Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.tethering" />
diff --git a/Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java
deleted file mode 100644
index 09f880b..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/CronetUrlRequestTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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 android.net.http.cts;
-
-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 android.net.http.cts.util.CronetCtsTestServer;
-import android.net.http.cts.util.TestStatusListener;
-import android.net.http.cts.util.TestUrlRequestCallback;
-import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
-
-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.UrlRequest.Status;
-import org.chromium.net.UrlResponseInfo;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class CronetUrlRequestTest {
- private static final String TAG = CronetUrlRequestTest.class.getSimpleName();
-
- @NonNull private CronetEngine mCronetEngine;
- @NonNull private TestUrlRequestCallback mCallback;
- @NonNull private ConnectivityManager mCm;
- @NonNull private CronetCtsTestServer mTestServer;
-
- @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();
- mCallback = new TestUrlRequestCallback();
- mTestServer = new CronetCtsTestServer(context);
- }
-
- @After
- public void tearDown() throws Exception {
- mCronetEngine.shutdown();
- mTestServer.shutdown();
- }
-
- 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 UrlRequest buildUrlRequest(String url) {
- return mCronetEngine.newUrlRequestBuilder(url, mCallback, mCallback.getExecutor()).build();
- }
-
- @Test
- public void testUrlRequestGet_CompletesSuccessfully() throws Exception {
- assertHasTestableNetworks();
- String url = mTestServer.getSuccessUrl();
- UrlRequest request = buildUrlRequest(url);
- request.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertEquals(
- "Unexpected http status code from " + url + ".", 200, info.getHttpStatusCode());
- assertGreaterThan(
- "Received byte from " + url + " is 0.", (int) info.getReceivedByteCount(), 0);
- }
-
- @Test
- public void testUrlRequestStatus_InvalidBeforeRequestStarts() throws Exception {
- UrlRequest request = buildUrlRequest(mTestServer.getSuccessUrl());
- // Calling before request is started should give Status.INVALID,
- // since the native adapter is not created.
- TestStatusListener statusListener = new TestStatusListener();
- request.getStatus(statusListener);
- statusListener.expectStatus(Status.INVALID);
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
new file mode 100644
index 0000000..6a8467c
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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.http.cts;
+
+import static android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
+import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.net.http.HttpEngine;
+import android.net.http.UrlRequest;
+import android.net.http.UrlResponseInfo;
+import android.net.http.cts.util.TestUrlRequestCallback;
+import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class HttpEngineTest {
+ private static final String HOST = "source.android.com";
+ private static final String URL = "https://" + HOST;
+
+ private HttpEngine.Builder mEngineBuilder;
+ private TestUrlRequestCallback mCallback;
+ private HttpEngine mEngine;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ skipIfNoInternetConnection(context);
+ mEngineBuilder = new HttpEngine.Builder(context);
+ mCallback = new TestUrlRequestCallback();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mEngine != null) {
+ mEngine.shutdown();
+ }
+ }
+
+ private boolean isQuic(String negotiatedProtocol) {
+ return negotiatedProtocol.startsWith("http/2+quic") || negotiatedProtocol.startsWith("h3");
+ }
+
+ @Test
+ public void testHttpEngine_Default() throws Exception {
+ mEngine = mEngineBuilder.build();
+ UrlRequest.Builder builder =
+ mEngine.newUrlRequestBuilder(URL, mCallback, mCallback.getExecutor());
+ builder.build().start();
+
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
+ assertEquals("h2", info.getNegotiatedProtocol());
+ }
+
+ @Test
+ public void testHttpEngine_DisableHttp2() throws Exception {
+ mEngine = mEngineBuilder.setEnableHttp2(false).build();
+ UrlRequest.Builder builder =
+ mEngine.newUrlRequestBuilder(URL, mCallback, mCallback.getExecutor());
+ builder.build().start();
+
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
+ assertEquals("http/1.1", info.getNegotiatedProtocol());
+ }
+
+ @Test
+ public void testHttpEngine_EnableQuic() throws Exception {
+ mEngine = mEngineBuilder.setEnableQuic(true).addQuicHint(HOST, 443, 443).build();
+ // The hint doesn't guarantee that QUIC will win the race, just that it will race TCP.
+ // We send multiple requests to reduce the flakiness of the test.
+ boolean quicWasUsed = false;
+ for (int i = 0; i < 5; i++) {
+ UrlRequest.Builder builder =
+ mEngine.newUrlRequestBuilder(URL, mCallback, mCallback.getExecutor());
+ builder.build().start();
+
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
+ quicWasUsed = isQuic(info.getNegotiatedProtocol());
+ if (quicWasUsed) {
+ break;
+ }
+ }
+ assertTrue(quicWasUsed);
+ }
+
+ @Test
+ public void testHttpEngine_GetDefaultUserAgent() throws Exception {
+ assertThat(mEngineBuilder.getDefaultUserAgent(), containsString("AndroidHttpClient"));
+ }
+}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
new file mode 100644
index 0000000..d7d3679
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.net.http.cts;
+
+import static android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
+import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+import android.content.Context;
+import android.net.http.HttpEngine;
+import android.net.http.UrlRequest;
+import android.net.http.UrlRequest.Status;
+import android.net.http.UrlResponseInfo;
+import android.net.http.cts.util.HttpCtsTestServer;
+import android.net.http.cts.util.TestStatusListener;
+import android.net.http.cts.util.TestUrlRequestCallback;
+import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UrlRequestTest {
+ private TestUrlRequestCallback mCallback;
+ private HttpCtsTestServer mTestServer;
+ private HttpEngine mHttpEngine;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ skipIfNoInternetConnection(context);
+ HttpEngine.Builder builder = new HttpEngine.Builder(context);
+ mHttpEngine = builder.build();
+ mCallback = new TestUrlRequestCallback();
+ mTestServer = new HttpCtsTestServer(context);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHttpEngine != null) {
+ mHttpEngine.shutdown();
+ }
+ if (mTestServer != null) {
+ mTestServer.shutdown();
+ }
+ }
+
+ private UrlRequest buildUrlRequest(String url) {
+ return mHttpEngine.newUrlRequestBuilder(url, mCallback, mCallback.getExecutor()).build();
+ }
+
+ @Test
+ public void testUrlRequestGet_CompletesSuccessfully() throws Exception {
+ String url = mTestServer.getSuccessUrl();
+ UrlRequest request = buildUrlRequest(url);
+ request.start();
+
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
+ assertThat("Received byte count must be > 0", info.getReceivedByteCount(), greaterThan(0L));
+ }
+
+ @Test
+ public void testUrlRequestStatus_InvalidBeforeRequestStarts() throws Exception {
+ UrlRequest request = buildUrlRequest(mTestServer.getSuccessUrl());
+ // Calling before request is started should give Status.INVALID,
+ // since the native adapter is not created.
+ TestStatusListener statusListener = new TestStatusListener();
+ request.getStatus(statusListener);
+ statusListener.expectStatus(Status.INVALID);
+ }
+}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/CronetCtsTestServer.kt b/Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt
similarity index 82%
rename from Cronet/tests/cts/src/android/net/http/cts/util/CronetCtsTestServer.kt
rename to Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt
index 3ccb571..87d5108 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/util/CronetCtsTestServer.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt
@@ -19,8 +19,8 @@
import android.content.Context
import android.webkit.cts.CtsTestServer
-/** Extends CtsTestServer to handle POST requests and other cronet specific test requests */
-class CronetCtsTestServer(context: Context) : CtsTestServer(context) {
+/** Extends CtsTestServer to handle POST requests and other test specific requests */
+class HttpCtsTestServer(context: Context) : CtsTestServer(context) {
val successUrl: String = getAssetUrl("html/hello_world.html")
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestStatusListener.kt b/Cronet/tests/cts/src/android/net/http/cts/util/TestStatusListener.kt
index 4d26ec0..e526c7d 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestStatusListener.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/TestStatusListener.kt
@@ -16,9 +16,9 @@
package android.net.http.cts.util
+import android.net.http.UrlRequest.StatusListener
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
-import org.chromium.net.UrlRequest.StatusListener
import org.junit.Assert.assertSame
private const val TIMEOUT_MS = 12000L
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
new file mode 100644
index 0000000..d047828
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
@@ -0,0 +1,294 @@
+/*
+ * 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.http.cts.util;
+
+import android.net.http.UploadDataProvider;
+import android.net.http.UploadDataSink;
+import android.os.ConditionVariable;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** An UploadDataProvider implementation used in tests. */
+public class TestUploadDataProvider extends UploadDataProvider {
+ // Indicates whether all success callbacks are synchronous or asynchronous.
+ // Doesn't apply to errors.
+ public enum SuccessCallbackMode {
+ SYNC,
+ ASYNC
+ }
+
+ // Indicates whether failures should throw exceptions, invoke callbacks
+ // synchronously, or invoke callback asynchronously.
+ public enum FailMode {
+ NONE,
+ THROWN,
+ CALLBACK_SYNC,
+ CALLBACK_ASYNC
+ }
+
+ private final ArrayList<byte[]> mReads = new ArrayList<byte[]>();
+ private final SuccessCallbackMode mSuccessCallbackMode;
+ private final Executor mExecutor;
+
+ private boolean mChunked;
+
+ // Index of read to fail on.
+ private int mReadFailIndex = -1;
+ // Indicates how to fail on a read.
+ private FailMode mReadFailMode = FailMode.NONE;
+
+ private FailMode mRewindFailMode = FailMode.NONE;
+
+ private FailMode mLengthFailMode = FailMode.NONE;
+
+ private int mNumReadCalls;
+ private int mNumRewindCalls;
+
+ private int mNextRead;
+ private boolean mStarted;
+ private boolean mReadPending;
+ private boolean mRewindPending;
+ // Used to ensure there are no read/rewind requests after a failure.
+ private boolean mFailed;
+
+ private final AtomicBoolean mClosed = new AtomicBoolean(false);
+ private final ConditionVariable mAwaitingClose = new ConditionVariable(false);
+
+ public TestUploadDataProvider(
+ SuccessCallbackMode successCallbackMode, final Executor executor) {
+ mSuccessCallbackMode = successCallbackMode;
+ mExecutor = executor;
+ }
+
+ // Adds the result to be returned by a successful read request. The
+ // returned bytes must all fit within the read buffer provided by Cronet.
+ // After a rewind, if there is one, all reads will be repeated.
+ public void addRead(byte[] read) {
+ if (mStarted) {
+ throw new IllegalStateException("Adding bytes after read");
+ }
+ mReads.add(read);
+ }
+
+ public void setReadFailure(int readFailIndex, FailMode readFailMode) {
+ mReadFailIndex = readFailIndex;
+ mReadFailMode = readFailMode;
+ }
+
+ public void setLengthFailure() {
+ mLengthFailMode = FailMode.THROWN;
+ }
+
+ public void setRewindFailure(FailMode rewindFailMode) {
+ mRewindFailMode = rewindFailMode;
+ }
+
+ public void setChunked(boolean chunked) {
+ mChunked = chunked;
+ }
+
+ public int getNumReadCalls() {
+ return mNumReadCalls;
+ }
+
+ public int getNumRewindCalls() {
+ return mNumRewindCalls;
+ }
+
+ /** Returns the cumulative length of all data added by calls to addRead. */
+ @Override
+ public long getLength() throws IOException {
+ if (mClosed.get()) {
+ throw new ClosedChannelException();
+ }
+ if (mLengthFailMode == FailMode.THROWN) {
+ throw new IllegalStateException("Sync length failure");
+ }
+ return getUploadedLength();
+ }
+
+ public long getUploadedLength() {
+ if (mChunked) {
+ return -1;
+ }
+ long length = 0;
+ for (byte[] read : mReads) {
+ length += read.length;
+ }
+ return length;
+ }
+
+ @Override
+ public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)
+ throws IOException {
+ int currentReadCall = mNumReadCalls;
+ ++mNumReadCalls;
+ if (mClosed.get()) {
+ throw new ClosedChannelException();
+ }
+ assertIdle();
+
+ if (maybeFailRead(currentReadCall, uploadDataSink)) {
+ mFailed = true;
+ return;
+ }
+
+ mReadPending = true;
+ mStarted = true;
+
+ final boolean finalChunk = (mChunked && mNextRead == mReads.size() - 1);
+ if (mNextRead < mReads.size()) {
+ if ((byteBuffer.limit() - byteBuffer.position()) < mReads.get(mNextRead).length) {
+ throw new IllegalStateException("Read buffer smaller than expected.");
+ }
+ byteBuffer.put(mReads.get(mNextRead));
+ ++mNextRead;
+ } else {
+ throw new IllegalStateException("Too many reads: " + mNextRead);
+ }
+
+ Runnable completeRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ mReadPending = false;
+ uploadDataSink.onReadSucceeded(finalChunk);
+ }
+ };
+ if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
+ completeRunnable.run();
+ } else {
+ mExecutor.execute(completeRunnable);
+ }
+ }
+
+ @Override
+ public void rewind(final UploadDataSink uploadDataSink) throws IOException {
+ ++mNumRewindCalls;
+ if (mClosed.get()) {
+ throw new ClosedChannelException();
+ }
+ assertIdle();
+
+ if (maybeFailRewind(uploadDataSink)) {
+ mFailed = true;
+ return;
+ }
+
+ if (mNextRead == 0) {
+ // Should never try and rewind when rewinding does nothing.
+ throw new IllegalStateException("Unexpected rewind when already at beginning");
+ }
+
+ mRewindPending = true;
+ mNextRead = 0;
+
+ Runnable completeRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ mRewindPending = false;
+ uploadDataSink.onRewindSucceeded();
+ }
+ };
+ if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
+ completeRunnable.run();
+ } else {
+ mExecutor.execute(completeRunnable);
+ }
+ }
+
+ private void assertIdle() {
+ if (mReadPending) {
+ throw new IllegalStateException("Unexpected operation during read");
+ }
+ if (mRewindPending) {
+ throw new IllegalStateException("Unexpected operation during rewind");
+ }
+ if (mFailed) {
+ throw new IllegalStateException("Unexpected operation after failure");
+ }
+ }
+
+ private boolean maybeFailRead(int readIndex, final UploadDataSink uploadDataSink) {
+ if (readIndex != mReadFailIndex) return false;
+
+ switch (mReadFailMode) {
+ case THROWN:
+ throw new IllegalStateException("Thrown read failure");
+ case CALLBACK_SYNC:
+ uploadDataSink.onReadError(new IllegalStateException("Sync read failure"));
+ return true;
+ case CALLBACK_ASYNC:
+ Runnable errorRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ uploadDataSink.onReadError(
+ new IllegalStateException("Async read failure"));
+ }
+ };
+ mExecutor.execute(errorRunnable);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean maybeFailRewind(final UploadDataSink uploadDataSink) {
+ switch (mRewindFailMode) {
+ case THROWN:
+ throw new IllegalStateException("Thrown rewind failure");
+ case CALLBACK_SYNC:
+ uploadDataSink.onRewindError(new IllegalStateException("Sync rewind failure"));
+ return true;
+ case CALLBACK_ASYNC:
+ Runnable errorRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ uploadDataSink.onRewindError(
+ new IllegalStateException("Async rewind failure"));
+ }
+ };
+ mExecutor.execute(errorRunnable);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (!mClosed.compareAndSet(false, true)) {
+ throw new AssertionError("Closed twice");
+ }
+ mAwaitingClose.open();
+ }
+
+ public void assertClosed() {
+ mAwaitingClose.block(5000);
+ if (!mClosed.get()) {
+ throw new AssertionError("Was not closed");
+ }
+ }
+}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
index c7143f5..0b9e90f 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
@@ -24,15 +24,14 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.http.CallbackException;
+import android.net.http.HttpException;
+import android.net.http.InlineExecutionProhibitedException;
+import android.net.http.UrlRequest;
+import android.net.http.UrlResponseInfo;
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;
@@ -50,7 +49,7 @@
public ArrayList<UrlResponseInfo> mRedirectResponseInfoList = new ArrayList<>();
public ArrayList<String> mRedirectUrlList = new ArrayList<>();
public UrlResponseInfo mResponseInfo;
- public CronetException mError;
+ public HttpException mError;
public ResponseStep mResponseStep = ResponseStep.NOTHING;
@@ -89,7 +88,7 @@
// Signaled on each step when mAutoAdvance is false.
private final ConditionVariable mStepBlock = new ConditionVariable();
- // Executor Service for Cronet callbacks.
+ // Executor Service for Http callbacks.
private final ExecutorService mExecutorService;
private Thread mExecutorThread;
@@ -349,7 +348,7 @@
}
@Override
- public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
+ public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException 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) {
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt b/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt
new file mode 100644
index 0000000..d30c059
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.http.cts.util
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.http.UrlResponseInfo
+import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeNotNull
+
+fun skipIfNoInternetConnection(context: Context) {
+ val connectivityManager = context.getSystemService(ConnectivityManager::class.java)
+ assumeNotNull(
+ "This test requires a working Internet connection", connectivityManager.getActiveNetwork())
+}
+
+fun assertOKStatusCode(info: UrlResponseInfo) {
+ assertEquals("Status code must be 200 OK", 200, info.getHttpStatusCode())
+}
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 172670e..6d17476 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -1,4 +1,3 @@
-chenbruce@google.com
chiachangwang@google.com
cken@google.com
huangaaron@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 700a085..a1e81c8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -64,6 +64,9 @@
"name": "connectivity_native_test"
},
{
+ "name": "CtsNetHttpTestCases"
+ },
+ {
"name": "libclat_test"
},
{
diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml
index b832e16..23467e7 100644
--- a/Tethering/AndroidManifest.xml
+++ b/Tethering/AndroidManifest.xml
@@ -43,7 +43,9 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
+ <!-- Sending non-protected broadcast from system uid is not allowed. -->
<protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
+ <protected-broadcast android:name="com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM" />
<application
android:process="com.android.networkstack.process"
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index cbdf0c0..4c677d0 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -27,14 +27,6 @@
// as cronet_defaults may have different values
// depending on the branch
-// cronet_java_defaults_enabled_srcs is used to specify the srcs of CronetJavaDefaultsEnabled
-// This is required until the external/cronet is auto-merged to tm-mainline-prod and
-// :cronet_aml_api_sources is available
-cronet_java_defaults_enabled_srcs = [":cronet_aml_api_sources"]
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_defaults may have different values
-// depending on the branch
-
java_sdk_library {
name: "framework-tethering",
defaults: [
@@ -82,7 +74,7 @@
java_defaults {
name: "CronetJavaDefaultsEnabled",
- srcs: cronet_java_defaults_enabled_srcs,
+ srcs: [":cronet_aml_api_sources"],
libs: [
"androidx.annotation_annotation",
],
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 63702f2..f90b3a4 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -1802,7 +1802,7 @@
TestNetworkAgent mobile, TestNetworkAgent wifi, TestNetworkAgent dun) throws Exception {
final NetworkCallback dunNetworkCallback = setupDunUpstreamTest(automatic, inOrder);
- // Pretend cellular connected and expect the upstream to be set.
+ // Pretend cellular connected and expect the upstream to be not set.
mobile.fakeConnect();
mCm.makeDefaultNetwork(mobile, BROADCAST_FIRST);
mLooper.dispatchAll();
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index c39269e..b7ca3af 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -21,6 +21,18 @@
#include <stdbool.h>
#include <stdint.h>
+// bionic kernel uapi linux/udp.h header is munged...
+#define __kernel_udphdr udphdr
+#include <linux/udp.h>
+
+// Offsets from beginning of L4 (TCP/UDP) header
+#define TCP_OFFSET(field) offsetof(struct tcphdr, field)
+#define UDP_OFFSET(field) offsetof(struct udphdr, field)
+
+// Offsets from beginning of L3 (IPv4/IPv6) header
+#define IP4_OFFSET(field) offsetof(struct iphdr, field)
+#define IP6_OFFSET(field) offsetof(struct ipv6hdr, field)
+
// this returns 0 iff skb->sk is NULL
static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) = (void*)BPF_FUNC_get_socket_cookie;
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 43920d0..84da79d 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -46,8 +46,9 @@
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)
+// Used for 'bool enable_tracing'
+static const bool TRACE_ON = true;
+static const bool TRACE_OFF = false;
// offsetof(struct iphdr, ihl) -- but that's a bitfield
#define IPPROTO_IHL_OFF 0
@@ -60,14 +61,18 @@
#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) \
- DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
- AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", false)
+#define DEFINE_BPF_MAP_NO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
+ AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", false, \
+ BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, /*ignore_on_eng*/false, \
+ /*ignore_on_user*/false, /*ignore_on_userdebug*/false)
// For maps netd only needs read only access to
-#define DEFINE_BPF_MAP_RO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
- DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
- AID_ROOT, AID_NET_BW_ACCT, 0460, "fs_bpf_netd_readonly", "", false)
+#define DEFINE_BPF_MAP_RO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
+ AID_ROOT, AID_NET_BW_ACCT, 0460, "fs_bpf_netd_readonly", "", false, \
+ BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, /*ignore_on_eng*/false, \
+ /*ignore_on_user*/false, /*ignore_on_userdebug*/false)
// For maps netd needs to be able to read and write
#define DEFINE_BPF_MAP_RW_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
@@ -95,6 +100,19 @@
/* never actually used from ebpf */
DEFINE_BPF_MAP_NO_NETD(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE)
+// A single-element configuration array, packet tracing is enabled when 'true'.
+DEFINE_BPF_MAP_EXT(packet_trace_enabled_map, ARRAY, uint32_t, bool, 1,
+ AID_ROOT, AID_SYSTEM, 0060, "fs_bpf_net_shared", "", false,
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, /*ignore_on_eng*/false,
+ /*ignore_on_user*/true, /*ignore_on_userdebug*/false)
+
+// A ring buffer on which packet information is pushed. This map will only be loaded
+// on eng and userdebug devices. User devices won't load this to save memory.
+DEFINE_BPF_RINGBUF_EXT(packet_trace_ringbuf, PacketTrace, PACKET_TRACE_BUF_SIZE,
+ AID_ROOT, AID_SYSTEM, 0060, "fs_bpf_net_shared", "", false,
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, /*ignore_on_eng*/false,
+ /*ignore_on_user*/true, /*ignore_on_userdebug*/false);
+
// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
// a full table dump, followed by an update in userspace, and then a reload into the kernel,
@@ -222,12 +240,72 @@
: bpf_skb_load_bytes(skb, L3_off, to, len);
}
+static __always_inline inline void do_packet_tracing(
+ const struct __sk_buff* const skb, const bool egress, const uint32_t uid,
+ const uint32_t tag, const bool enable_tracing, const unsigned kver) {
+ if (!enable_tracing) return;
+ if (kver < KVER(5, 8, 0)) return;
+
+ uint32_t mapKey = 0;
+ bool* traceConfig = bpf_packet_trace_enabled_map_lookup_elem(&mapKey);
+ if (traceConfig == NULL) return;
+ if (*traceConfig == false) return;
+
+ PacketTrace* pkt = bpf_packet_trace_ringbuf_reserve();
+ if (pkt == NULL) return;
+
+ // Errors from bpf_skb_load_bytes_net are ignored to favor returning something
+ // over returning nothing. In the event of an error, the kernel will fill in
+ // zero for the destination memory. Do not change the default '= 0' below.
+
+ uint8_t proto = 0;
+ uint8_t L4_off = 0;
+ uint8_t ipVersion = 0;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ (void)bpf_skb_load_bytes_net(skb, IP4_OFFSET(protocol), &proto, sizeof(proto), kver);
+ (void)bpf_skb_load_bytes_net(skb, IPPROTO_IHL_OFF, &L4_off, sizeof(L4_off), kver);
+ L4_off = (L4_off & 0x0F) * 4; // IHL calculation.
+ ipVersion = 4;
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ (void)bpf_skb_load_bytes_net(skb, IP6_OFFSET(nexthdr), &proto, sizeof(proto), kver);
+ L4_off = sizeof(struct ipv6hdr);
+ ipVersion = 6;
+ }
+
+ uint8_t flags = 0;
+ __be16 sport = 0, dport = 0;
+ if (proto == IPPROTO_TCP && L4_off >= 20) {
+ (void)bpf_skb_load_bytes_net(skb, L4_off + TCP_FLAG32_OFF + 1, &flags, sizeof(flags), kver);
+ (void)bpf_skb_load_bytes_net(skb, L4_off + TCP_OFFSET(source), &sport, sizeof(sport), kver);
+ (void)bpf_skb_load_bytes_net(skb, L4_off + TCP_OFFSET(dest), &dport, sizeof(dport), kver);
+ } else if (proto == IPPROTO_UDP && L4_off >= 20) {
+ (void)bpf_skb_load_bytes_net(skb, L4_off + UDP_OFFSET(source), &sport, sizeof(sport), kver);
+ (void)bpf_skb_load_bytes_net(skb, L4_off + UDP_OFFSET(dest), &dport, sizeof(dport), kver);
+ }
+
+ pkt->timestampNs = bpf_ktime_get_boot_ns();
+ pkt->ifindex = skb->ifindex;
+ pkt->length = skb->len;
+
+ pkt->uid = uid;
+ pkt->tag = tag;
+ pkt->sport = sport;
+ pkt->dport = dport;
+
+ pkt->egress = egress;
+ pkt->ipProto = proto;
+ pkt->tcpFlags = flags;
+ pkt->ipVersion = ipVersion;
+
+ bpf_packet_trace_ringbuf_submit(pkt);
+}
+
static __always_inline inline bool skip_owner_match(struct __sk_buff* skb, const unsigned kver) {
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), kver);
+ (void)bpf_skb_load_bytes_net(skb, IP4_OFFSET(protocol), &proto, sizeof(proto), kver);
if (proto == IPPROTO_ESP) return true;
if (proto != IPPROTO_TCP) return false; // handles read failure above
uint8_t ihl;
@@ -243,7 +321,7 @@
} 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), kver);
+ (void)bpf_skb_load_bytes_net(skb, IP6_OFFSET(nexthdr), &proto, sizeof(proto), kver);
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.
@@ -315,6 +393,7 @@
}
static __always_inline inline int bpf_traffic_account(struct __sk_buff* skb, bool egress,
+ const bool enable_tracing,
const unsigned kver) {
uint32_t sock_uid = bpf_get_socket_uid(skb);
uint64_t cookie = bpf_get_socket_cookie(skb);
@@ -374,34 +453,51 @@
key.tag = 0;
}
+ do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
update_stats_with_config(skb, egress, &key, *selectedMap);
update_app_uid_stats_map(skb, egress, &uid);
asm("%0 &= 1" : "+r"(match));
return match;
}
+DEFINE_BPF_PROG_EXT("cgroupskb/ingress/stats$trace", AID_ROOT, AID_SYSTEM,
+ bpf_cgroup_ingress_trace, KVER(5, 8, 0), KVER_INF,
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, false,
+ "fs_bpf_netd_readonly", "", false, true, false)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, INGRESS, TRACE_ON, KVER(5, 8, 0));
+}
+
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, INGRESS, KVER(4, 19, 0));
+ return bpf_traffic_account(skb, INGRESS, TRACE_OFF, KVER(4, 19, 0));
}
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, INGRESS, KVER_NONE);
+ return bpf_traffic_account(skb, INGRESS, TRACE_OFF, KVER_NONE);
+}
+
+DEFINE_BPF_PROG_EXT("cgroupskb/egress/stats$trace", AID_ROOT, AID_SYSTEM,
+ bpf_cgroup_egress_trace, KVER(5, 8, 0), KVER_INF,
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, false,
+ "fs_bpf_netd_readonly", "", false, true, false)
+(struct __sk_buff* skb) {
+ return bpf_traffic_account(skb, EGRESS, TRACE_ON, KVER(5, 8, 0));
}
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, KVER(4, 19, 0));
+ return bpf_traffic_account(skb, EGRESS, TRACE_OFF, KVER(4, 19, 0));
}
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, KVER_NONE);
+ return bpf_traffic_account(skb, EGRESS, TRACE_OFF, KVER_NONE);
}
// WARNING: Android T's non-updatable netd depends on the name of this program.
diff --git a/bpf_progs/netd.h b/bpf_progs/netd.h
index cc88680..be604f9 100644
--- a/bpf_progs/netd.h
+++ b/bpf_progs/netd.h
@@ -69,6 +69,24 @@
uint64_t tcpTxPackets;
} Stats;
+typedef struct {
+ uint64_t timestampNs;
+ uint32_t ifindex;
+ uint32_t length;
+
+ uint32_t uid;
+ uint32_t tag;
+
+ __be16 sport;
+ __be16 dport;
+
+ bool egress;
+ uint8_t ipProto;
+ uint8_t tcpFlags;
+ uint8_t ipVersion; // 4=IPv4, 6=IPv6, 0=unknown
+} PacketTrace;
+STRUCT_SIZE(PacketTrace, 8+4+4 + 4+4 + 2+2 + 1+1+1+1);
+
// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
// of MEM_LOCK granted to netd. The memory space needed by each map can be calculated by the
@@ -87,7 +105,8 @@
// dozable_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
// standby_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
// powersave_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
-// total: 4930Kbytes
+// packet_trace_ringbuf:key: 0 bytes, value: 24 bytes, cost: 32768 bytes = 32Kbytes
+// total: 4962Kbytes
// It takes maximum 4.9MB kernel memory space if all maps are full, which requires any devices
// running this module to have a memlock rlimit to be larger then 5MB. In the old qtaguid module,
// we don't have a total limit for data entries but only have limitation of tags each uid can have.
@@ -102,6 +121,7 @@
static const int IFACE_STATS_MAP_SIZE = 1000;
static const int CONFIGURATION_MAP_SIZE = 2;
static const int UID_OWNER_MAP_SIZE = 4000;
+static const int PACKET_TRACE_BUF_SIZE = 32 * 1024;
#ifdef __cplusplus
@@ -145,6 +165,8 @@
#define CONFIGURATION_MAP_PATH BPF_NETD_PATH "map_netd_configuration_map"
#define UID_OWNER_MAP_PATH BPF_NETD_PATH "map_netd_uid_owner_map"
#define UID_PERMISSION_MAP_PATH BPF_NETD_PATH "map_netd_uid_permission_map"
+#define PACKET_TRACE_RINGBUF_PATH BPF_NETD_PATH "map_netd_packet_trace_ringbuf"
+#define PACKET_TRACE_ENABLED_MAP_PATH BPF_NETD_PATH "map_netd_packet_trace_enabled_map"
#endif // __cplusplus
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index eb77288..5532853 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -192,15 +192,20 @@
method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
method public void registerService(@NonNull android.net.nsd.NsdServiceInfo, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.RegistrationListener);
- method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
- method public void resolveService(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ResolveListener);
+ method public void registerServiceInfoCallback(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ServiceInfoCallback);
+ method @Deprecated public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
+ method @Deprecated public void resolveService(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ResolveListener);
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
+ method public void stopServiceResolution(@NonNull android.net.nsd.NsdManager.ResolveListener);
method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
+ method public void unregisterServiceInfoCallback(@NonNull android.net.nsd.NsdManager.ServiceInfoCallback);
field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
field public static final String EXTRA_NSD_STATE = "nsd_state";
field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3
+ field public static final int FAILURE_BAD_PARAMETERS = 6; // 0x6
field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0
field public static final int FAILURE_MAX_LIMIT = 4; // 0x4
+ field public static final int FAILURE_OPERATION_NOT_RUNNING = 5; // 0x5
field public static final int NSD_STATE_DISABLED = 1; // 0x1
field public static final int NSD_STATE_ENABLED = 2; // 0x2
field public static final int PROTOCOL_DNS_SD = 1; // 0x1
@@ -224,21 +229,32 @@
public static interface NsdManager.ResolveListener {
method public void onResolveFailed(android.net.nsd.NsdServiceInfo, int);
+ method public default void onResolveStopped(@NonNull android.net.nsd.NsdServiceInfo);
method public void onServiceResolved(android.net.nsd.NsdServiceInfo);
+ method public default void onStopResolutionFailed(@NonNull android.net.nsd.NsdServiceInfo, int);
+ }
+
+ public static interface NsdManager.ServiceInfoCallback {
+ method public void onServiceInfoCallbackRegistrationFailed(int);
+ method public void onServiceInfoCallbackUnregistered();
+ method public void onServiceLost();
+ method public void onServiceUpdated(@NonNull android.net.nsd.NsdServiceInfo);
}
public final class NsdServiceInfo implements android.os.Parcelable {
ctor public NsdServiceInfo();
method public int describeContents();
method public java.util.Map<java.lang.String,byte[]> getAttributes();
- method public java.net.InetAddress getHost();
+ method @Deprecated public java.net.InetAddress getHost();
+ method @NonNull public java.util.List<java.net.InetAddress> getHostAddresses();
method @Nullable public android.net.Network getNetwork();
method public int getPort();
method public String getServiceName();
method public String getServiceType();
method public void removeAttribute(String);
method public void setAttribute(String, String);
- method public void setHost(java.net.InetAddress);
+ method @Deprecated public void setHost(java.net.InetAddress);
+ method public void setHostAddresses(@NonNull java.util.List<java.net.InetAddress>);
method public void setNetwork(@Nullable android.net.Network);
method public void setPort(int);
method public void setServiceName(String);
diff --git a/framework-t/src/android/net/nsd/INsdManagerCallback.aidl b/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
index 1a262ec..d89bfa9 100644
--- a/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
+++ b/framework-t/src/android/net/nsd/INsdManagerCallback.aidl
@@ -36,4 +36,10 @@
void onUnregisterServiceSucceeded(int listenerKey);
void onResolveServiceFailed(int listenerKey, int error);
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
+ void onStopResolutionFailed(int listenerKey, int error);
+ void onStopResolutionSucceeded(int listenerKey);
+ void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error);
+ void onServiceUpdated(int listenerKey, in NsdServiceInfo info);
+ void onServiceUpdatedLost(int listenerKey);
+ void onServiceInfoCallbackUnregistered(int listenerKey);
}
diff --git a/framework-t/src/android/net/nsd/INsdServiceConnector.aidl b/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
index b06ae55..5533154 100644
--- a/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
+++ b/framework-t/src/android/net/nsd/INsdServiceConnector.aidl
@@ -32,4 +32,7 @@
void stopDiscovery(int listenerKey);
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
void startDaemon();
+ void stopResolution(int listenerKey);
+ void registerServiceInfoCallback(int listenerKey, in NsdServiceInfo serviceInfo);
+ void unregisterServiceInfoCallback(int listenerKey);
}
\ No newline at end of file
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index d340384..122e3a0 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,6 +16,7 @@
package android.net.nsd;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -44,6 +45,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -230,7 +233,6 @@
/** @hide */
public static final int DAEMON_CLEANUP = 18;
-
/** @hide */
public static final int DAEMON_STARTUP = 19;
@@ -243,9 +245,28 @@
public static final int UNREGISTER_CLIENT = 22;
/** @hide */
- public static final int MDNS_MONITORING_SOCKETS_CLEANUP = 23;
+ public static final int MDNS_DISCOVERY_MANAGER_EVENT = 23;
+
/** @hide */
- public static final int MDNS_DISCOVERY_MANAGER_EVENT = 24;
+ public static final int STOP_RESOLUTION = 24;
+ /** @hide */
+ public static final int STOP_RESOLUTION_FAILED = 25;
+ /** @hide */
+ public static final int STOP_RESOLUTION_SUCCEEDED = 26;
+
+ /** @hide */
+ public static final int REGISTER_SERVICE_CALLBACK = 27;
+ /** @hide */
+ public static final int REGISTER_SERVICE_CALLBACK_FAILED = 28;
+ /** @hide */
+ public static final int SERVICE_UPDATED = 29;
+ /** @hide */
+ public static final int SERVICE_UPDATED_LOST = 30;
+
+ /** @hide */
+ public static final int UNREGISTER_SERVICE_CALLBACK = 31;
+ /** @hide */
+ public static final int UNREGISTER_SERVICE_CALLBACK_SUCCEEDED = 32;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -272,6 +293,15 @@
EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP");
EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP");
EVENT_NAMES.put(MDNS_SERVICE_EVENT, "MDNS_SERVICE_EVENT");
+ EVENT_NAMES.put(STOP_RESOLUTION, "STOP_RESOLUTION");
+ EVENT_NAMES.put(STOP_RESOLUTION_FAILED, "STOP_RESOLUTION_FAILED");
+ EVENT_NAMES.put(STOP_RESOLUTION_SUCCEEDED, "STOP_RESOLUTION_SUCCEEDED");
+ EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK, "REGISTER_SERVICE_CALLBACK");
+ EVENT_NAMES.put(REGISTER_SERVICE_CALLBACK_FAILED, "REGISTER_SERVICE_CALLBACK_FAILED");
+ EVENT_NAMES.put(SERVICE_UPDATED, "SERVICE_UPDATED");
+ EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK, "UNREGISTER_SERVICE_CALLBACK");
+ EVENT_NAMES.put(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED,
+ "UNREGISTER_SERVICE_CALLBACK_SUCCEEDED");
}
/** @hide */
@@ -597,6 +627,36 @@
public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info);
}
+
+ @Override
+ public void onStopResolutionFailed(int listenerKey, int error) {
+ sendError(STOP_RESOLUTION_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onStopResolutionSucceeded(int listenerKey) {
+ sendNoArg(STOP_RESOLUTION_SUCCEEDED, listenerKey);
+ }
+
+ @Override
+ public void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
+ sendError(REGISTER_SERVICE_CALLBACK_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onServiceUpdated(int listenerKey, NsdServiceInfo info) {
+ sendInfo(SERVICE_UPDATED, listenerKey, info);
+ }
+
+ @Override
+ public void onServiceUpdatedLost(int listenerKey) {
+ sendNoArg(SERVICE_UPDATED_LOST, listenerKey);
+ }
+
+ @Override
+ public void onServiceInfoCallbackUnregistered(int listenerKey) {
+ sendNoArg(UNREGISTER_SERVICE_CALLBACK_SUCCEEDED, listenerKey);
+ }
}
/**
@@ -620,6 +680,37 @@
*/
public static final int FAILURE_MAX_LIMIT = 4;
+ /**
+ * Indicates that the stop operation failed because it is not running.
+ * This failure is passed with {@link ResolveListener#onStopResolutionFailed}.
+ */
+ public static final int FAILURE_OPERATION_NOT_RUNNING = 5;
+
+ /**
+ * Indicates that the service has failed to resolve because of bad parameters.
+ *
+ * This failure is passed with
+ * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed}.
+ */
+ public static final int FAILURE_BAD_PARAMETERS = 6;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ FAILURE_OPERATION_NOT_RUNNING,
+ })
+ public @interface StopOperationFailureCode {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ FAILURE_ALREADY_ACTIVE,
+ FAILURE_BAD_PARAMETERS,
+ })
+ public @interface ResolutionFailureCode {
+ }
+
/** Interface for callback invocation for service discovery */
public interface DiscoveryListener {
@@ -648,12 +739,97 @@
public void onServiceUnregistered(NsdServiceInfo serviceInfo);
}
- /** Interface for callback invocation for service resolution */
+ /**
+ * Callback for use with {@link NsdManager#resolveService} to resolve the service info and use
+ * with {@link NsdManager#stopServiceResolution} to stop resolution.
+ */
public interface ResolveListener {
- public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode);
+ /**
+ * Called on the internal thread or with an executor passed to
+ * {@link NsdManager#resolveService} to report the resolution was failed with an error.
+ *
+ * A resolution operation would call either onServiceResolved or onResolveFailed once based
+ * on the result.
+ */
+ void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode);
- public void onServiceResolved(NsdServiceInfo serviceInfo);
+ /**
+ * Called on the internal thread or with an executor passed to
+ * {@link NsdManager#resolveService} to report the resolved service info.
+ *
+ * A resolution operation would call either onServiceResolved or onResolveFailed once based
+ * on the result.
+ */
+ void onServiceResolved(NsdServiceInfo serviceInfo);
+
+ /**
+ * Called on the internal thread or with an executor passed to
+ * {@link NsdManager#resolveService} to report the resolution was stopped.
+ *
+ * A stop resolution operation would call either onResolveStopped or onStopResolutionFailed
+ * once based on the result.
+ */
+ default void onResolveStopped(@NonNull NsdServiceInfo serviceInfo) { }
+
+ /**
+ * Called once on the internal thread or with an executor passed to
+ * {@link NsdManager#resolveService} to report that stopping resolution failed with an
+ * error.
+ *
+ * A stop resolution operation would call either onResolveStopped or onStopResolutionFailed
+ * once based on the result.
+ */
+ default void onStopResolutionFailed(@NonNull NsdServiceInfo serviceInfo,
+ @StopOperationFailureCode int errorCode) { }
+ }
+
+ /**
+ * Callback to listen to service info updates.
+ *
+ * For use with {@link NsdManager#registerServiceInfoCallback} to register, and with
+ * {@link NsdManager#unregisterServiceInfoCallback} to stop listening.
+ */
+ public interface ServiceInfoCallback {
+
+ /**
+ * Reports that registering the callback failed with an error.
+ *
+ * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}.
+ *
+ * onServiceInfoCallbackRegistrationFailed will be called exactly once when the callback
+ * could not be registered. No other callback will be sent in that case.
+ */
+ void onServiceInfoCallbackRegistrationFailed(@ResolutionFailureCode int errorCode);
+
+ /**
+ * Reports updated service info.
+ *
+ * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. Any
+ * service updates will be notified via this callback until
+ * {@link NsdManager#unregisterServiceInfoCallback} is called. This will only be called once
+ * the service is found, so may never be called if the service is never present.
+ */
+ void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo);
+
+ /**
+ * Reports when the service that this callback listens to becomes unavailable.
+ *
+ * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}. The
+ * service may become available again, in which case {@link #onServiceUpdated} will be
+ * called.
+ */
+ void onServiceLost();
+
+ /**
+ * Reports that service info updates have stopped.
+ *
+ * Called on the executor passed to {@link NsdManager#registerServiceInfoCallback}.
+ *
+ * A callback unregistration operation will call onServiceInfoCallbackUnregistered
+ * once. After this, the callback may be reused.
+ */
+ void onServiceInfoCallbackUnregistered();
}
@VisibleForTesting
@@ -746,6 +922,33 @@
executor.execute(() -> ((ResolveListener) listener).onServiceResolved(
(NsdServiceInfo) obj));
break;
+ case STOP_RESOLUTION_FAILED:
+ removeListener(key);
+ executor.execute(() -> ((ResolveListener) listener).onStopResolutionFailed(
+ ns, errorCode));
+ break;
+ case STOP_RESOLUTION_SUCCEEDED:
+ removeListener(key);
+ executor.execute(() -> ((ResolveListener) listener).onResolveStopped(
+ ns));
+ break;
+ case REGISTER_SERVICE_CALLBACK_FAILED:
+ removeListener(key);
+ executor.execute(() -> ((ServiceInfoCallback) listener)
+ .onServiceInfoCallbackRegistrationFailed(errorCode));
+ break;
+ case SERVICE_UPDATED:
+ executor.execute(() -> ((ServiceInfoCallback) listener)
+ .onServiceUpdated((NsdServiceInfo) obj));
+ break;
+ case SERVICE_UPDATED_LOST:
+ executor.execute(() -> ((ServiceInfoCallback) listener).onServiceLost());
+ break;
+ case UNREGISTER_SERVICE_CALLBACK_SUCCEEDED:
+ removeListener(key);
+ executor.execute(() -> ((ServiceInfoCallback) listener)
+ .onServiceInfoCallbackUnregistered());
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -1057,7 +1260,14 @@
* @param serviceInfo service to be resolved
* @param listener to receive callback upon success or failure. Cannot be null.
* Cannot be in use for an active service resolution.
+ *
+ * @deprecated the returned ServiceInfo may get stale at any time after resolution, including
+ * immediately after the callback is called, and may not contain some service information that
+ * could be delivered later, like additional host addresses. Prefer using
+ * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the
+ * state of the service.
*/
+ @Deprecated
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
resolveService(serviceInfo, Runnable::run, listener);
}
@@ -1069,7 +1279,14 @@
* @param serviceInfo service to be resolved
* @param executor Executor to run listener callbacks with
* @param listener to receive callback upon success or failure.
+ *
+ * @deprecated the returned ServiceInfo may get stale at any time after resolution, including
+ * immediately after the callback is called, and may not contain some service information that
+ * could be delivered later, like additional host addresses. Prefer using
+ * {@link #registerServiceInfoCallback}, which will keep the application up-to-date with the
+ * state of the service.
*/
+ @Deprecated
public void resolveService(@NonNull NsdServiceInfo serviceInfo,
@NonNull Executor executor, @NonNull ResolveListener listener) {
checkServiceInfo(serviceInfo);
@@ -1081,6 +1298,85 @@
}
}
+ /**
+ * Stop service resolution initiated with {@link #resolveService}.
+ *
+ * A successful stop is notified with a call to {@link ResolveListener#onResolveStopped}.
+ *
+ * <p> Upon failure to stop service resolution for example if resolution is done or the
+ * requester stops resolution repeatedly, the application is notified
+ * {@link ResolveListener#onStopResolutionFailed} with {@link #FAILURE_OPERATION_NOT_RUNNING}
+ *
+ * @param listener This should be a listener object that was passed to {@link #resolveService}.
+ * It identifies the resolution that should be stopped and notifies of a
+ * successful or unsuccessful stop. Throws {@code IllegalArgumentException} if
+ * the listener was not passed to resolveService before.
+ */
+ public void stopServiceResolution(@NonNull ResolveListener listener) {
+ int id = getListenerKey(listener);
+ try {
+ mService.stopResolution(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Register a callback to listen for updates to a service.
+ *
+ * An application can listen to a service to continuously monitor availability of given service.
+ * The callback methods will be called on the passed executor. And service updates are sent with
+ * continuous calls to {@link ServiceInfoCallback#onServiceUpdated}.
+ *
+ * This is different from {@link #resolveService} which provides one shot service information.
+ *
+ * <p> An application can listen to a service once a time. It needs to cancel the registration
+ * before registering other callbacks. Upon failure to register a callback for example if
+ * it's a duplicated registration, the application is notified through
+ * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with
+ * {@link #FAILURE_BAD_PARAMETERS} or {@link #FAILURE_ALREADY_ACTIVE}.
+ *
+ * @param serviceInfo the service to receive updates for
+ * @param executor Executor to run callbacks with
+ * @param listener to receive callback upon service update
+ */
+ public void registerServiceInfoCallback(@NonNull NsdServiceInfo serviceInfo,
+ @NonNull Executor executor, @NonNull ServiceInfoCallback listener) {
+ checkServiceInfo(serviceInfo);
+ int key = putListener(listener, executor, serviceInfo);
+ try {
+ mService.registerServiceInfoCallback(key, serviceInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister a callback registered with {@link #registerServiceInfoCallback}.
+ *
+ * A successful unregistration is notified with a call to
+ * {@link ServiceInfoCallback#onServiceInfoCallbackUnregistered}. The same callback can only be
+ * reused after this is called.
+ *
+ * <p>If the callback is not already registered, this will throw with
+ * {@link IllegalArgumentException}.
+ *
+ * @param listener This should be a listener object that was passed to
+ * {@link #registerServiceInfoCallback}. It identifies the registration that
+ * should be unregistered and notifies of a successful or unsuccessful stop.
+ * Throws {@code IllegalArgumentException} if the listener was not passed to
+ * {@link #registerServiceInfoCallback} before.
+ */
+ public void unregisterServiceInfoCallback(@NonNull ServiceInfoCallback listener) {
+ // Will throw IllegalArgumentException if the listener is not known
+ int id = getListenerKey(listener);
+ try {
+ mService.unregisterServiceInfoCallback(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private static void checkListener(Object listener) {
Objects.requireNonNull(listener, "listener cannot be null");
}
diff --git a/framework-t/src/android/net/nsd/NsdServiceInfo.java b/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 6438a60..caeecdd 100644
--- a/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -26,10 +26,14 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.net.module.util.InetAddressUtils;
+
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
/**
@@ -46,7 +50,7 @@
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
- private InetAddress mHost;
+ private final List<InetAddress> mHostAddresses = new ArrayList<>();
private int mPort;
@@ -84,17 +88,32 @@
mServiceType = s;
}
- /** Get the host address. The host address is valid for a resolved service. */
+ /**
+ * Get the host address. The host address is valid for a resolved service.
+ *
+ * @deprecated Use {@link #getHostAddresses()} to get the entire list of addresses for the host.
+ */
+ @Deprecated
public InetAddress getHost() {
- return mHost;
+ return mHostAddresses.size() == 0 ? null : mHostAddresses.get(0);
}
- /** Set the host address */
+ /**
+ * Set the host address
+ *
+ * @deprecated Use {@link #setHostAddresses(List)} to set multiple addresses for the host.
+ */
+ @Deprecated
public void setHost(InetAddress s) {
- mHost = s;
+ setHostAddresses(Collections.singletonList(s));
}
- /** Get port number. The port number is valid for a resolved service. */
+ /**
+ * Get port number. The port number is valid for a resolved service.
+ *
+ * The port is valid for all addresses.
+ * @see #getHostAddresses()
+ */
public int getPort() {
return mPort;
}
@@ -105,6 +124,24 @@
}
/**
+ * Get the host addresses.
+ *
+ * All host addresses are valid for the resolved service.
+ * All addresses share the same port
+ * @see #getPort()
+ */
+ @NonNull
+ public List<InetAddress> getHostAddresses() {
+ return new ArrayList<>(mHostAddresses);
+ }
+
+ /** Set the host addresses */
+ public void setHostAddresses(@NonNull List<InetAddress> addresses) {
+ mHostAddresses.clear();
+ mHostAddresses.addAll(addresses);
+ }
+
+ /**
* Unpack txt information from a base-64 encoded byte array.
*
* @param txtRecordsRawBytes The raw base64 encoded byte array.
@@ -359,7 +396,7 @@
StringBuilder sb = new StringBuilder();
sb.append("name: ").append(mServiceName)
.append(", type: ").append(mServiceType)
- .append(", host: ").append(mHost)
+ .append(", hostAddresses: ").append(TextUtils.join(", ", mHostAddresses))
.append(", port: ").append(mPort)
.append(", network: ").append(mNetwork);
@@ -377,12 +414,6 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mServiceName);
dest.writeString(mServiceType);
- if (mHost != null) {
- dest.writeInt(1);
- dest.writeByteArray(mHost.getAddress());
- } else {
- dest.writeInt(0);
- }
dest.writeInt(mPort);
// TXT record key/value pairs.
@@ -401,6 +432,10 @@
dest.writeParcelable(mNetwork, 0);
dest.writeInt(mInterfaceIndex);
+ dest.writeInt(mHostAddresses.size());
+ for (InetAddress address : mHostAddresses) {
+ InetAddressUtils.parcelInetAddress(dest, address, flags);
+ }
}
/** Implement the Parcelable interface */
@@ -410,13 +445,6 @@
NsdServiceInfo info = new NsdServiceInfo();
info.mServiceName = in.readString();
info.mServiceType = in.readString();
-
- if (in.readInt() == 1) {
- try {
- info.mHost = InetAddress.getByAddress(in.createByteArray());
- } catch (java.net.UnknownHostException e) {}
- }
-
info.mPort = in.readInt();
// TXT record key/value pairs.
@@ -432,6 +460,10 @@
}
info.mNetwork = in.readParcelable(null, Network.class);
info.mInterfaceIndex = in.readInt();
+ int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ info.mHostAddresses.add(InetAddressUtils.unparcelInetAddress(in));
+ }
return info;
}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index dd3404c..0b03983 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -470,7 +470,9 @@
}
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ method public final void start(@IntRange(from=0xa, to=0xe10) int, int);
field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf
+ field public static final int FLAG_AUTOMATIC_ON_OFF = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0
}
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 7b6e769..7db231e 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -188,7 +188,7 @@
void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId,
int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
- String dstAddr);
+ String dstAddr, boolean automaticOnOffKeepalives);
void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds,
in ISocketKeepaliveCallback cb);
diff --git a/framework/src/android/net/NattSocketKeepalive.java b/framework/src/android/net/NattSocketKeepalive.java
index 56cc923..4d45e70 100644
--- a/framework/src/android/net/NattSocketKeepalive.java
+++ b/framework/src/android/net/NattSocketKeepalive.java
@@ -47,13 +47,39 @@
mResourceId = resourceId;
}
+ /**
+ * Request that keepalive be started with the given {@code intervalSec}.
+ *
+ * When a VPN is running with the network for this keepalive as its underlying network, the
+ * system can monitor the TCP connections on that VPN to determine whether this keepalive is
+ * necessary. To enable this behavior, pass {@link SocketKeepalive#FLAG_AUTOMATIC_ON_OFF} into
+ * the flags. When this is enabled, the system will disable sending keepalive packets when
+ * there are no TCP connections over the VPN(s) running over this network to save battery, and
+ * restart sending them as soon as any TCP connection is opened over one of the VPN networks.
+ * When no VPN is running on top of this network, this flag has no effect, i.e. the keepalives
+ * are always sent with the specified interval.
+ *
+ * Also {@see SocketKeepalive}.
+ *
+ * @param intervalSec The target interval in seconds between keepalive packet transmissions.
+ * The interval should be between 10 seconds and 3600 seconds. Otherwise,
+ * the supplied {@link Callback} will see a call to
+ * {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
+ * @param flags Flags to enable/disable available options on this keepalive.
+ * @hide
+ */
@Override
- protected void startImpl(int intervalSec) {
+ protected void startImpl(int intervalSec, int flags) {
+ if (0 != (flags & ~FLAG_AUTOMATIC_ON_OFF)) {
+ throw new IllegalArgumentException("Illegal flag value for "
+ + this.getClass().getSimpleName() + " : " + flags);
+ }
+ final boolean automaticOnOffKeepalives = 0 != (flags & FLAG_AUTOMATIC_ON_OFF);
mExecutor.execute(() -> {
try {
mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId,
- intervalSec, mCallback,
- mSource.getHostAddress(), mDestination.getHostAddress());
+ intervalSec, mCallback, mSource.getHostAddress(),
+ mDestination.getHostAddress(), automaticOnOffKeepalives);
} catch (RemoteException e) {
Log.e(TAG, "Error starting socket keepalive: ", e);
throw e.rethrowFromSystemServer();
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 1486619..732bd87 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -483,6 +483,20 @@
*/
public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
+ /**
+ * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
+ * automatic keepalive request.
+ *
+ * NATT keepalives have an automatic mode where the system only sends keepalive packets when
+ * TCP sockets are open over a VPN. The system will check periodically for presence of
+ * such open sockets, and this message is what triggers the re-evaluation.
+ *
+ * arg1 = hardware slot number of the keepalive
+ * obj = {@link Network} that the keepalive is started on.
+ * @hide
+ */
+ public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 30;
+
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
config.legacyTypeName, config.legacySubTypeName);
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index e07601f..e70d75d 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -18,6 +18,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
+import static com.android.net.module.util.BitUtils.describeDifferences;
import android.annotation.IntDef;
import android.annotation.LongDef;
@@ -2069,30 +2070,14 @@
* 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.
+ * this never returns the empty string. See BitUtils#describeDifferences.
* @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();
+ return describeDifferences(oldCaps, mNetworkCapabilities,
+ NetworkCapabilities::capabilityNameOf);
}
/**
diff --git a/framework/src/android/net/SocketKeepalive.java b/framework/src/android/net/SocketKeepalive.java
index 57cf5e3..90e5e9b 100644
--- a/framework/src/android/net/SocketKeepalive.java
+++ b/framework/src/android/net/SocketKeepalive.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
+
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -174,6 +176,27 @@
public @interface KeepaliveEvent {}
/**
+ * Whether the system automatically toggles keepalive when no TCP connection is open on the VPN.
+ *
+ * If this flag is present, the system will monitor the VPN(s) running on top of the specified
+ * network for open TCP connections. When no such connections are open, it will turn off the
+ * keepalives to conserve battery power. When there is at least one such connection it will
+ * turn on the keepalives to make sure functionality is preserved.
+ *
+ * This only works with {@link NattSocketKeepalive}.
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_AUTOMATIC_ON_OFF = 1 << 0;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "FLAG_"}, flag = true, value = {
+ FLAG_AUTOMATIC_ON_OFF
+ })
+ public @interface StartFlags {}
+
+ /**
* The minimum interval in seconds between keepalive packet transmissions.
*
* @hide
@@ -294,13 +317,15 @@
}
/**
- * Request that keepalive be started with the given {@code intervalSec}. See
- * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
- * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
- * thrown into the {@code executor}. This is typically not important to catch because the remote
- * party is the system, so if it is not in shape to communicate through binder the system is
- * probably going down anyway. If the caller cares regardless, it can use a custom
- * {@link Executor} to catch the {@link RemoteException}.
+ * Request that keepalive be started with the given {@code intervalSec}.
+ *
+ * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
+ * exception when invoking start or stop of the {@link SocketKeepalive}, a
+ * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
+ * {@link Executor}. This is typically not important to catch because the remote party is
+ * the system, so if it is not in shape to communicate through binder the system is going
+ * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
+ * {@link RuntimeException}.
*
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
* The interval should be between 10 seconds and 3600 seconds, otherwise
@@ -308,11 +333,35 @@
*/
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
int intervalSec) {
- startImpl(intervalSec);
+ startImpl(intervalSec, 0 /* flags */);
+ }
+
+ /**
+ * Request that keepalive be started with the given {@code intervalSec}.
+ *
+ * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
+ * exception when invoking start or stop of the {@link SocketKeepalive}, a
+ * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
+ * {@link Executor}. This is typically not important to catch because the remote party is
+ * the system, so if it is not in shape to communicate through binder the system is going
+ * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
+ * {@link RuntimeException}.
+ *
+ * @param intervalSec The target interval in seconds between keepalive packet transmissions.
+ * The interval should be between 10 seconds and 3600 seconds. Otherwise,
+ * the supplied {@link Callback} will see a call to
+ * {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
+ * @param flags Flags to enable/disable available options on this keepalive.
+ * @hide
+ */
+ @SystemApi(client = PRIVILEGED_APPS)
+ public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
+ int intervalSec, @StartFlags int flags) {
+ startImpl(intervalSec, flags);
}
/** @hide */
- protected abstract void startImpl(int intervalSec);
+ protected abstract void startImpl(int intervalSec, @StartFlags int flags);
/**
* Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
diff --git a/framework/src/android/net/TcpSocketKeepalive.java b/framework/src/android/net/TcpSocketKeepalive.java
index 7131784..51d805e 100644
--- a/framework/src/android/net/TcpSocketKeepalive.java
+++ b/framework/src/android/net/TcpSocketKeepalive.java
@@ -50,7 +50,11 @@
* acknowledgement.
*/
@Override
- protected void startImpl(int intervalSec) {
+ protected void startImpl(int intervalSec, int flags) {
+ if (0 != flags) {
+ throw new IllegalArgumentException("Illegal flag value for "
+ + this.getClass().getSimpleName() + " : " + flags);
+ }
mExecutor.execute(() -> {
try {
mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback);
diff --git a/service-t/jni/com_android_server_net_NetworkStatsService.cpp b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
index 39cbaf7..af0b8d8 100644
--- a/service-t/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/service-t/jni/com_android_server_net_NetworkStatsService.cpp
@@ -30,9 +30,11 @@
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
+#include "netdbpf/NetworkTraceHandler.h"
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
+using android::bpf::NetworkTraceHandler;
namespace android {
@@ -67,7 +69,7 @@
}
}
-static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
+static jlong nativeGetTotalStat(JNIEnv* env, jclass clazz, jint type) {
Stats stats = {};
if (bpfGetIfaceStats(NULL, &stats) == 0) {
@@ -77,7 +79,7 @@
}
}
-static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
+static jlong nativeGetIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) {
return UNKNOWN;
@@ -92,7 +94,7 @@
}
}
-static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
+static jlong nativeGetUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
Stats stats = {};
if (bpfGetUidStats(uid, &stats) == 0) {
@@ -102,10 +104,15 @@
}
}
+static void nativeInitNetworkTracing(JNIEnv* env, jclass clazz) {
+ NetworkTraceHandler::InitPerfettoTracing();
+}
+
static const JNINativeMethod gMethods[] = {
- {"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
- {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
- {"nativeGetUidStat", "(II)J", (void*)getUidStat},
+ {"nativeGetTotalStat", "(I)J", (void*)nativeGetTotalStat},
+ {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)nativeGetIfaceStat},
+ {"nativeGetUidStat", "(II)J", (void*)nativeGetUidStat},
+ {"nativeInitNetworkTracing", "()V", (void*)nativeInitNetworkTracing},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
diff --git a/service-t/native/libs/libnetworkstats/Android.bp b/service-t/native/libs/libnetworkstats/Android.bp
index 5b3d314..aa1ee41 100644
--- a/service-t/native/libs/libnetworkstats/Android.bp
+++ b/service-t/native/libs/libnetworkstats/Android.bp
@@ -24,12 +24,19 @@
host_supported: false,
header_libs: ["bpf_connectivity_headers"],
srcs: [
- "BpfNetworkStats.cpp"
+ "BpfNetworkStats.cpp",
+ "NetworkTraceHandler.cpp",
],
shared_libs: [
"libbase",
"liblog",
],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_static_lib_headers: [
+ "libperfetto_client_experimental",
+ ],
export_include_dirs: ["include"],
cflags: [
"-Wall",
@@ -54,6 +61,7 @@
header_libs: ["bpf_connectivity_headers"],
srcs: [
"BpfNetworkStatsTest.cpp",
+ "NetworkTraceHandlerTest.cpp",
],
cflags: [
"-Wall",
@@ -64,10 +72,12 @@
static_libs: [
"libgmock",
"libnetworkstats",
+ "libperfetto_client_experimental",
],
shared_libs: [
"libbase",
"liblog",
+ "libandroid_net",
],
compile_multilib: "both",
multilib: {
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
new file mode 100644
index 0000000..4c37b8d
--- /dev/null
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "NetworkTrace"
+
+#include "netdbpf/NetworkTraceHandler.h"
+
+#include <arpa/inet.h>
+#include <bpf/BpfUtils.h>
+#include <log/log.h>
+#include <perfetto/config/android/network_trace_config.pbzero.h>
+#include <perfetto/trace/android/network_trace.pbzero.h>
+#include <perfetto/trace/profiling/profile_packet.pbzero.h>
+#include <perfetto/tracing/platform.h>
+#include <perfetto/tracing/tracing.h>
+
+// Note: this is initializing state for a templated Perfetto type that resides
+// in the `perfetto` namespace. This must be defined in the global scope.
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::bpf::NetworkTraceHandler);
+
+namespace android {
+namespace bpf {
+using ::perfetto::protos::pbzero::NetworkPacketEvent;
+using ::perfetto::protos::pbzero::NetworkPacketTraceConfig;
+using ::perfetto::protos::pbzero::TracePacket;
+using ::perfetto::protos::pbzero::TrafficDirection;
+
+// static
+void NetworkTraceHandler::RegisterDataSource() {
+ ALOGD("Registering Perfetto data source");
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name("android.network_packets");
+ NetworkTraceHandler::Register(dsd);
+}
+
+// static
+void NetworkTraceHandler::InitPerfettoTracing() {
+ perfetto::TracingInitArgs args = {};
+ args.backends |= perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+ NetworkTraceHandler::RegisterDataSource();
+}
+
+NetworkTraceHandler::NetworkTraceHandler()
+ : NetworkTraceHandler([this](const PacketTrace& pkt) {
+ NetworkTraceHandler::Trace(
+ [this, pkt](NetworkTraceHandler::TraceContext ctx) {
+ Fill(pkt, *ctx.NewTracePacket());
+ });
+ }) {}
+
+void NetworkTraceHandler::OnSetup(const SetupArgs& args) {
+ const std::string& raw = args.config->network_packet_trace_config_raw();
+ NetworkPacketTraceConfig::Decoder config(raw);
+
+ mPollMs = config.poll_ms();
+ if (mPollMs < 100) {
+ ALOGI("poll_ms is missing or below the 100ms minimum. Increasing to 100ms");
+ mPollMs = 100;
+ }
+}
+
+void NetworkTraceHandler::OnStart(const StartArgs&) {
+ if (!Start()) return;
+ mTaskRunner = perfetto::Platform::GetDefaultPlatform()->CreateTaskRunner({});
+ Loop();
+}
+
+void NetworkTraceHandler::OnStop(const StopArgs&) {
+ Stop();
+ mTaskRunner.reset();
+}
+
+void NetworkTraceHandler::Loop() {
+ mTaskRunner->PostDelayedTask([this]() { Loop(); }, mPollMs);
+ ConsumeAll();
+}
+
+void NetworkTraceHandler::Fill(const PacketTrace& src, TracePacket& dst) {
+ dst.set_timestamp(src.timestampNs);
+ auto* event = dst.set_network_packet();
+ event->set_direction(src.egress ? TrafficDirection::DIR_EGRESS
+ : TrafficDirection::DIR_INGRESS);
+ event->set_length(src.length);
+ event->set_uid(src.uid);
+ event->set_tag(src.tag);
+
+ event->set_local_port(src.egress ? ntohs(src.sport) : ntohs(src.dport));
+ event->set_remote_port(src.egress ? ntohs(src.dport) : ntohs(src.sport));
+
+ event->set_ip_proto(src.ipProto);
+ event->set_tcp_flags(src.tcpFlags);
+
+ char ifname[IF_NAMESIZE] = {};
+ if (if_indextoname(src.ifindex, ifname) == ifname) {
+ event->set_interface(std::string(ifname));
+ } else {
+ event->set_interface("error");
+ }
+}
+
+bool NetworkTraceHandler::Start() {
+ ALOGD("Starting datasource");
+
+ auto status = mConfigurationMap.init(PACKET_TRACE_ENABLED_MAP_PATH);
+ if (!status.ok()) {
+ ALOGW("Failed to bind config map: %s", status.error().message().c_str());
+ return false;
+ }
+
+ auto rb = BpfRingbuf<PacketTrace>::Create(PACKET_TRACE_RINGBUF_PATH);
+ if (!rb.ok()) {
+ ALOGW("Failed to create ringbuf: %s", rb.error().message().c_str());
+ return false;
+ }
+
+ mRingBuffer = std::move(*rb);
+
+ auto res = mConfigurationMap.writeValue(0, true, BPF_ANY);
+ if (!res.ok()) {
+ ALOGW("Failed to enable tracing: %s", res.error().message().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool NetworkTraceHandler::Stop() {
+ ALOGD("Stopping datasource");
+
+ auto res = mConfigurationMap.writeValue(0, false, BPF_ANY);
+ if (!res.ok()) {
+ ALOGW("Failed to disable tracing: %s", res.error().message().c_str());
+ return false;
+ }
+
+ mRingBuffer.reset();
+
+ return true;
+}
+
+bool NetworkTraceHandler::ConsumeAll() {
+ if (mRingBuffer == nullptr) {
+ ALOGW("Tracing is not active");
+ return false;
+ }
+
+ base::Result<int> ret = mRingBuffer->ConsumeAll(mCallback);
+ if (!ret.ok()) {
+ ALOGW("Failed to poll ringbuf: %s", ret.error().message().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
new file mode 100644
index 0000000..760ae91
--- /dev/null
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandlerTest.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 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 <android-base/unique_fd.h>
+#include <android/multinetwork.h>
+#include <arpa/inet.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "netdbpf/NetworkTraceHandler.h"
+
+using ::testing::AllOf;
+using ::testing::AnyOf;
+using ::testing::Each;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Test;
+
+namespace android {
+namespace bpf {
+
+__be16 bindAndListen(int s) {
+ sockaddr_in sin = {.sin_family = AF_INET};
+ socklen_t len = sizeof(sin);
+ if (bind(s, (sockaddr*)&sin, sizeof(sin))) return 0;
+ if (listen(s, 1)) return 0;
+ if (getsockname(s, (sockaddr*)&sin, &len)) return 0;
+ return sin.sin_port;
+}
+
+// This takes tcp flag constants from the standard library and makes them usable
+// with the flags we get from BPF. The standard library flags are big endian
+// whereas the BPF flags are reported in host byte order. BPF also trims the
+// flags down to the 8 single-bit flag bits (fin, syn, rst, etc).
+constexpr inline uint8_t FlagToHost(__be32 be_unix_flags) {
+ return ntohl(be_unix_flags) >> 16;
+}
+
+// Pretty prints all fields for a list of packets (useful for debugging).
+struct PacketPrinter {
+ const std::vector<PacketTrace>& data;
+ static constexpr char kTcpFlagNames[] = "FSRPAUEC";
+
+ friend std::ostream& operator<<(std::ostream& os, const PacketPrinter& d) {
+ os << "Packet count: " << d.data.size();
+ for (const PacketTrace& info : d.data) {
+ os << "\nifidx=" << info.ifindex;
+ os << ", len=" << info.length;
+ os << ", uid=" << info.uid;
+ os << ", tag=" << info.tag;
+ os << ", sport=" << info.sport;
+ os << ", dport=" << info.dport;
+ os << ", direction=" << (info.egress ? "egress" : "ingress");
+ os << ", proto=" << static_cast<int>(info.ipProto);
+ os << ", ip=" << static_cast<int>(info.ipVersion);
+ os << ", flags=";
+ for (int i = 0; i < 8; i++) {
+ os << ((info.tcpFlags & (1 << i)) ? kTcpFlagNames[i] : '.');
+ }
+ }
+ return os;
+ }
+};
+
+class NetworkTraceHandlerTest : public testing::Test {
+ protected:
+ void SetUp() {
+ if (access(PACKET_TRACE_RINGBUF_PATH, R_OK)) {
+ GTEST_SKIP() << "Network tracing is not enabled/loaded on this build";
+ }
+ }
+};
+
+TEST_F(NetworkTraceHandlerTest, PollWhileInactive) {
+ NetworkTraceHandler handler([&](const PacketTrace& pkt) {});
+
+ // One succeed after start and before stop.
+ EXPECT_FALSE(handler.ConsumeAll());
+ ASSERT_TRUE(handler.Start());
+ EXPECT_TRUE(handler.ConsumeAll());
+ ASSERT_TRUE(handler.Stop());
+ EXPECT_FALSE(handler.ConsumeAll());
+}
+
+TEST_F(NetworkTraceHandlerTest, TraceTcpSession) {
+ __be16 server_port = 0;
+ std::vector<PacketTrace> packets;
+
+ // Record all packets with the bound address and current uid. This callback is
+ // involked only within ConsumeAll, at which point the port should have
+ // already been filled in and all packets have been processed.
+ NetworkTraceHandler handler([&](const PacketTrace& pkt) {
+ if (pkt.sport != server_port && pkt.dport != server_port) return;
+ if (pkt.uid != getuid()) return;
+ packets.push_back(pkt);
+ });
+
+ ASSERT_TRUE(handler.Start());
+ const uint32_t kClientTag = 2468;
+ const uint32_t kServerTag = 1357;
+
+ // Go through a typical connection sequence between two v4 sockets using tcp.
+ // This covers connection handshake, shutdown, and one data packet.
+ {
+ android::base::unique_fd clientsocket(socket(AF_INET, SOCK_STREAM, 0));
+ ASSERT_NE(-1, clientsocket) << "Failed to open client socket";
+ ASSERT_EQ(android_tag_socket(clientsocket, kClientTag), 0);
+
+ android::base::unique_fd serversocket(socket(AF_INET, SOCK_STREAM, 0));
+ ASSERT_NE(-1, serversocket) << "Failed to open server socket";
+ ASSERT_EQ(android_tag_socket(serversocket, kServerTag), 0);
+
+ server_port = bindAndListen(serversocket);
+ ASSERT_NE(0, server_port) << "Can't bind to server port";
+
+ sockaddr_in addr = {.sin_family = AF_INET, .sin_port = server_port};
+ ASSERT_EQ(0, connect(clientsocket, (sockaddr*)&addr, sizeof(addr)))
+ << "connect to loopback failed: " << strerror(errno);
+
+ int accepted = accept(serversocket, nullptr, nullptr);
+ ASSERT_NE(-1, accepted) << "accept connection failed: " << strerror(errno);
+
+ const char data[] = "abcdefghijklmnopqrstuvwxyz";
+ EXPECT_EQ(send(clientsocket, data, sizeof(data), 0), sizeof(data))
+ << "failed to send message: " << strerror(errno);
+
+ char buff[100] = {};
+ EXPECT_EQ(recv(accepted, buff, sizeof(buff), 0), sizeof(data))
+ << "failed to receive message: " << strerror(errno);
+
+ EXPECT_EQ(std::string(data), std::string(buff));
+ }
+
+ ASSERT_TRUE(handler.ConsumeAll());
+ ASSERT_TRUE(handler.Stop());
+
+ // There are 12 packets in total (6 messages: each seen by client & server):
+ // 1. Client connects to server with syn
+ // 2. Server responds with syn ack
+ // 3. Client responds with ack
+ // 4. Client sends data with psh ack
+ // 5. Server acks the data packet
+ // 6. Client closes connection with fin ack
+ ASSERT_EQ(packets.size(), 12) << PacketPrinter{packets};
+
+ // All packets should be TCP packets.
+ EXPECT_THAT(packets, Each(Field(&PacketTrace::ipProto, Eq(IPPROTO_TCP))));
+
+ // Packet 1: client requests connection with server.
+ EXPECT_EQ(packets[0].egress, 1) << PacketPrinter{packets};
+ EXPECT_EQ(packets[0].dport, server_port) << PacketPrinter{packets};
+ EXPECT_EQ(packets[0].tag, kClientTag) << PacketPrinter{packets};
+ EXPECT_EQ(packets[0].tcpFlags, FlagToHost(TCP_FLAG_SYN))
+ << PacketPrinter{packets};
+
+ // Packet 2: server receives request from client.
+ EXPECT_EQ(packets[1].egress, 0) << PacketPrinter{packets};
+ EXPECT_EQ(packets[1].dport, server_port) << PacketPrinter{packets};
+ EXPECT_EQ(packets[1].tag, kServerTag) << PacketPrinter{packets};
+ EXPECT_EQ(packets[1].tcpFlags, FlagToHost(TCP_FLAG_SYN))
+ << PacketPrinter{packets};
+
+ // Packet 3: server replies back with syn ack.
+ EXPECT_EQ(packets[2].egress, 1) << PacketPrinter{packets};
+ EXPECT_EQ(packets[2].sport, server_port) << PacketPrinter{packets};
+ EXPECT_EQ(packets[2].tcpFlags, FlagToHost(TCP_FLAG_SYN | TCP_FLAG_ACK))
+ << PacketPrinter{packets};
+
+ // Packet 4: client receives the server's syn ack.
+ EXPECT_EQ(packets[3].egress, 0) << PacketPrinter{packets};
+ EXPECT_EQ(packets[3].sport, server_port) << PacketPrinter{packets};
+ EXPECT_EQ(packets[3].tcpFlags, FlagToHost(TCP_FLAG_SYN | TCP_FLAG_ACK))
+ << PacketPrinter{packets};
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
new file mode 100644
index 0000000..c257aa0
--- /dev/null
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTraceHandler.h
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <perfetto/base/task_runner.h>
+#include <perfetto/tracing.h>
+
+#include <string>
+#include <unordered_map>
+
+#include "bpf/BpfMap.h"
+#include "bpf/BpfRingbuf.h"
+
+// For PacketTrace struct definition
+#include "netd.h"
+
+namespace android {
+namespace bpf {
+
+class NetworkTraceHandler : public perfetto::DataSource<NetworkTraceHandler> {
+ public:
+ // Registers this DataSource.
+ static void RegisterDataSource();
+
+ // Connects to the system Perfetto daemon and registers the trace handler.
+ static void InitPerfettoTracing();
+
+ // Initialize with the default Perfetto callback.
+ NetworkTraceHandler();
+
+ // Testonly: initialize with a callback capable of intercepting data.
+ NetworkTraceHandler(std::function<void(const PacketTrace&)> callback)
+ : mCallback(std::move(callback)) {}
+
+ // Testonly: standalone functions without perfetto dependency.
+ bool Start();
+ bool Stop();
+ bool ConsumeAll();
+
+ // perfetto::DataSource overrides:
+ void OnSetup(const SetupArgs&) override;
+ void OnStart(const StartArgs&) override;
+ void OnStop(const StopArgs&) override;
+
+ // Convert a PacketTrace into a Perfetto trace packet.
+ void Fill(const PacketTrace& src,
+ ::perfetto::protos::pbzero::TracePacket& dst);
+
+ private:
+ void Loop();
+
+ // How often to poll the ring buffer, defined by the trace config.
+ uint32_t mPollMs;
+
+ // The function to process PacketTrace, typically a Perfetto sink.
+ std::function<void(const PacketTrace&)> mCallback;
+
+ // The BPF ring buffer handle.
+ std::unique_ptr<BpfRingbuf<PacketTrace>> mRingBuffer;
+
+ // The packet tracing config map (really a 1-element array).
+ BpfMap<uint32_t, bool> mConfigurationMap;
+
+ // This must be the last member, causing it to be the first deleted. If it is
+ // not, members required for callbacks can be deleted before it's stopped.
+ std::unique_ptr<perfetto::base::TaskRunner> mTaskRunner;
+};
+
+} // namespace bpf
+} // namespace android
diff --git a/service-t/src/com/android/server/NetworkStatsServiceInitializer.java b/service-t/src/com/android/server/NetworkStatsServiceInitializer.java
index 0ea126a..82a4fbd 100644
--- a/service-t/src/com/android/server/NetworkStatsServiceInitializer.java
+++ b/service-t/src/com/android/server/NetworkStatsServiceInitializer.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.TrafficStats;
+import android.os.Build;
import android.util.Log;
import com.android.modules.utils.build.SdkLevel;
@@ -46,6 +47,15 @@
/* allowIsolated= */ false);
TrafficStats.init(getContext());
}
+
+ // The following code registers the Perfetto Network Trace Handler on non-user builds.
+ // The enhanced tracing is intended to be used for debugging and diagnosing issues. This
+ // is conditional on the build type rather than `isDebuggable` to match the system_server
+ // selinux rules which only allow the Perfetto connection under the same circumstances.
+ if (SdkLevel.isAtLeastU() && !Build.TYPE.equals("user")) {
+ Log.i(TAG, "Initializing network tracing hooks");
+ NetworkStatsService.nativeInitNetworkTracing();
+ }
}
@Override
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 36c3cd4..5dcf860 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.mdns.aidl.DiscoveryInfo;
@@ -59,6 +60,7 @@
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.server.connectivity.mdns.ExecutorProvider;
+import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
import com.android.server.connectivity.mdns.MdnsSearchOptions;
@@ -73,8 +75,15 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -87,10 +96,22 @@
public class NsdService extends INsdManager.Stub {
private static final String TAG = "NsdService";
private static final String MDNS_TAG = "mDnsConnector";
+ /**
+ * Enable discovery using the Java DiscoveryManager, instead of the legacy mdnsresponder
+ * implementation.
+ */
private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
private static final String LOCAL_DOMAIN_NAME = "local";
+ // Max label length as per RFC 1034/1035
+ private static final int MAX_LABEL_LENGTH = 63;
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ /**
+ * Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
+ * implementation.
+ */
+ private static final String MDNS_ADVERTISER_VERSION = "mdns_advertiser_version";
+
+ public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
@@ -104,6 +125,8 @@
private final MdnsDiscoveryManager mMdnsDiscoveryManager;
@Nullable
private final MdnsSocketProvider mMdnsSocketProvider;
+ @Nullable
+ private final MdnsAdvertiser mAdvertiser;
// WARNING : Accessing these values in any thread is not safe, it must only be changed in the
// state machine thread. If change this outside state machine, it will need to introduce
// synchronization.
@@ -196,6 +219,21 @@
}
}
+ private class ResolutionListener extends MdnsListener {
+
+ ResolutionListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
+ @NonNull String listenServiceType) {
+ super(clientId, transactionId, reqServiceInfo, listenServiceType);
+ }
+
+ @Override
+ public void onServiceFound(MdnsServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
+ NsdManager.RESOLVE_SERVICE_SUCCEEDED,
+ new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ }
+ }
+
/**
* Data class of mdns service callback information.
*/
@@ -328,7 +366,7 @@
mLegacyClientCount -= 1;
}
}
- if (mMdnsDiscoveryManager != null) {
+ if (mMdnsDiscoveryManager != null || mAdvertiser != null) {
maybeStopMonitoringSocketsIfNoActiveRequest();
}
maybeScheduleStop();
@@ -368,6 +406,20 @@
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
+ case NsdManager.STOP_RESOLUTION:
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onStopResolutionFailed(
+ clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
+ }
+ break;
+ case NsdManager.REGISTER_SERVICE_CALLBACK:
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onServiceInfoCallbackRegistrationFailed(
+ clientId, NsdManager.FAILURE_BAD_PARAMETERS);
+ }
+ break;
case NsdManager.DAEMON_CLEANUP:
maybeStopDaemon();
break;
@@ -382,9 +434,6 @@
maybeStartDaemon();
}
break;
- case NsdManager.MDNS_MONITORING_SOCKETS_CLEANUP:
- maybeStopMonitoringSockets();
- break;
default:
Log.e(TAG, "Unhandled " + msg);
return NOT_HANDLED;
@@ -432,6 +481,7 @@
clientInfo.mClientRequests.delete(clientId);
mIdToClientInfoMap.remove(globalId);
maybeScheduleStop();
+ maybeStopMonitoringSocketsIfNoActiveRequest();
}
private void storeListenerMap(int clientId, int transactionId, MdnsListener listener,
@@ -439,7 +489,6 @@
clientInfo.mClientIds.put(clientId, transactionId);
clientInfo.mListeners.put(clientId, listener);
mIdToClientInfoMap.put(transactionId, clientInfo);
- removeMessages(NsdManager.MDNS_MONITORING_SOCKETS_CLEANUP);
}
private void removeListenerMap(int clientId, int transactionId, ClientInfo clientInfo) {
@@ -449,6 +498,11 @@
maybeStopMonitoringSocketsIfNoActiveRequest();
}
+ private void clearRegisteredServiceInfo(ClientInfo clientInfo) {
+ clientInfo.mRegisteredService = null;
+ clientInfo.mClientIdForServiceUpdates = 0;
+ }
+
/**
* Check the given service type is valid and construct it to a service type
* which can use for discovery / resolution service.
@@ -471,8 +525,31 @@
final Matcher matcher = serviceTypePattern.matcher(serviceType);
if (!matcher.matches()) return null;
return matcher.group(1) == null
- ? serviceType + ".local"
- : matcher.group(1) + "._sub" + matcher.group(2) + ".local";
+ ? serviceType
+ : matcher.group(1) + "_sub." + matcher.group(2);
+ }
+
+ /**
+ * Truncate a service name to up to 63 UTF-8 bytes.
+ *
+ * See RFC6763 4.1.1: service instance names are UTF-8 and up to 63 bytes. Truncating
+ * names used in registerService follows historical behavior (see mdnsresponder
+ * handle_regservice_request).
+ */
+ @NonNull
+ private String truncateServiceName(@NonNull String originalName) {
+ // UTF-8 is at most 4 bytes per character; return early in the common case where
+ // the name can't possibly be over the limit given its string length.
+ if (originalName.length() <= MAX_LABEL_LENGTH / 4) return originalName;
+
+ final Charset utf8 = StandardCharsets.UTF_8;
+ final CharsetEncoder encoder = utf8.newEncoder();
+ final ByteBuffer out = ByteBuffer.allocate(MAX_LABEL_LENGTH);
+ // encode will write as many characters as possible to the out buffer, and just
+ // return an overflow code if there were too many characters (no need to check the
+ // return code here, this method truncates the name on purpose).
+ encoder.encode(CharBuffer.wrap(originalName), out, true /* endOfInput */);
+ return new String(out.array(), 0, out.position(), utf8);
}
@Override
@@ -482,7 +559,7 @@
final int clientId = msg.arg2;
final ListenerArgs args;
switch (msg.what) {
- case NsdManager.DISCOVER_SERVICES:
+ case NsdManager.DISCOVER_SERVICES: {
if (DBG) Log.d(TAG, "Discover services");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
@@ -510,14 +587,16 @@
break;
}
+ final String listenServiceType = serviceType + ".local";
maybeStartMonitoringSockets();
final MdnsListener listener =
- new DiscoveryListener(clientId, id, info, serviceType);
+ new DiscoveryListener(clientId, id, info, listenServiceType);
final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
.setNetwork(info.getNetwork())
.setIsPassiveMode(true)
.build();
- mMdnsDiscoveryManager.registerListener(serviceType, listener, options);
+ mMdnsDiscoveryManager.registerListener(
+ listenServiceType, listener, options);
storeListenerMap(clientId, id, listener, clientInfo);
clientInfo.onDiscoverServicesStarted(clientId, info);
} else {
@@ -536,6 +615,7 @@
}
}
break;
+ }
case NsdManager.STOP_DISCOVERY:
if (DBG) Log.d(TAG, "Stop service discovery");
args = (ListenerArgs) msg.obj;
@@ -594,16 +674,36 @@
break;
}
- maybeStartDaemon();
id = getUniqueId();
- if (registerService(id, args.serviceInfo)) {
- if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
+ if (mAdvertiser != null) {
+ final NsdServiceInfo serviceInfo = args.serviceInfo;
+ final String serviceType = serviceInfo.getServiceType();
+ final String registerServiceType = constructServiceType(serviceType);
+ if (registerServiceType == null) {
+ Log.e(TAG, "Invalid service type: " + serviceType);
+ clientInfo.onRegisterServiceFailed(clientId,
+ NsdManager.FAILURE_INTERNAL_ERROR);
+ break;
+ }
+ serviceInfo.setServiceType(registerServiceType);
+ serviceInfo.setServiceName(truncateServiceName(
+ serviceInfo.getServiceName()));
+
+ maybeStartMonitoringSockets();
+ mAdvertiser.addService(id, serviceInfo);
storeRequestMap(clientId, id, clientInfo, msg.what);
- // Return success after mDns reports success
} else {
- unregisterService(id);
- clientInfo.onRegisterServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ maybeStartDaemon();
+ if (registerService(id, args.serviceInfo)) {
+ if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
+ // Return success after mDns reports success
+ } else {
+ unregisterService(id);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
+
}
break;
case NsdManager.UNREGISTER_SERVICE:
@@ -619,14 +719,20 @@
}
id = clientInfo.mClientIds.get(clientId);
removeRequestMap(clientId, id, clientInfo);
- if (unregisterService(id)) {
+
+ if (mAdvertiser != null) {
+ mAdvertiser.removeService(id);
clientInfo.onUnregisterServiceSucceeded(clientId);
} else {
- clientInfo.onUnregisterServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ if (unregisterService(id)) {
+ clientInfo.onUnregisterServiceSucceeded(clientId);
+ } else {
+ clientInfo.onUnregisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
}
break;
- case NsdManager.RESOLVE_SERVICE:
+ case NsdManager.RESOLVE_SERVICE: {
if (DBG) Log.d(TAG, "Resolve service");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
@@ -638,8 +744,82 @@
break;
}
- if (clientInfo.mResolvedService != null) {
- clientInfo.onResolveServiceFailed(
+ final NsdServiceInfo info = args.serviceInfo;
+ id = getUniqueId();
+ if (mMdnsDiscoveryManager != null) {
+ final String serviceType = constructServiceType(info.getServiceType());
+ if (serviceType == null) {
+ clientInfo.onResolveServiceFailed(clientId,
+ NsdManager.FAILURE_INTERNAL_ERROR);
+ break;
+ }
+ final String resolveServiceType = serviceType + ".local";
+
+ maybeStartMonitoringSockets();
+ final MdnsListener listener =
+ new ResolutionListener(clientId, id, info, resolveServiceType);
+ final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
+ .setNetwork(info.getNetwork())
+ .setIsPassiveMode(true)
+ .build();
+ mMdnsDiscoveryManager.registerListener(
+ resolveServiceType, listener, options);
+ storeListenerMap(clientId, id, listener, clientInfo);
+ } else {
+ if (clientInfo.mResolvedService != null) {
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
+ break;
+ }
+
+ maybeStartDaemon();
+ if (resolveService(id, args.serviceInfo)) {
+ clientInfo.mResolvedService = new NsdServiceInfo();
+ storeRequestMap(clientId, id, clientInfo, msg.what);
+ } else {
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
+ }
+ break;
+ }
+ case NsdManager.STOP_RESOLUTION:
+ if (DBG) Log.d(TAG, "Stop service resolution");
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
+ // If the binder death notification for a INsdManagerCallback was received
+ // before any calls are received by NsdService, the clientInfo would be
+ // cleared and cause NPE. Add a null check here to prevent this corner case.
+ if (clientInfo == null) {
+ Log.e(TAG, "Unknown connector in stop resolution");
+ break;
+ }
+
+ id = clientInfo.mClientIds.get(clientId);
+ removeRequestMap(clientId, id, clientInfo);
+ if (stopResolveService(id)) {
+ clientInfo.onStopResolutionSucceeded(clientId);
+ } else {
+ clientInfo.onStopResolutionFailed(
+ clientId, NsdManager.FAILURE_OPERATION_NOT_RUNNING);
+ }
+ clientInfo.mResolvedService = null;
+ // TODO: Implement the stop resolution with MdnsDiscoveryManager.
+ break;
+ case NsdManager.REGISTER_SERVICE_CALLBACK:
+ if (DBG) Log.d(TAG, "Register a service callback");
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
+ // If the binder death notification for a INsdManagerCallback was received
+ // before any calls are received by NsdService, the clientInfo would be
+ // cleared and cause NPE. Add a null check here to prevent this corner case.
+ if (clientInfo == null) {
+ Log.e(TAG, "Unknown connector in callback registration");
+ break;
+ }
+
+ if (clientInfo.mRegisteredService != null) {
+ clientInfo.onServiceInfoCallbackRegistrationFailed(
clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
@@ -647,13 +827,35 @@
maybeStartDaemon();
id = getUniqueId();
if (resolveService(id, args.serviceInfo)) {
- clientInfo.mResolvedService = new NsdServiceInfo();
+ clientInfo.mRegisteredService = new NsdServiceInfo();
+ clientInfo.mClientIdForServiceUpdates = clientId;
storeRequestMap(clientId, id, clientInfo, msg.what);
} else {
- clientInfo.onResolveServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onServiceInfoCallbackRegistrationFailed(
+ clientId, NsdManager.FAILURE_BAD_PARAMETERS);
}
break;
+ case NsdManager.UNREGISTER_SERVICE_CALLBACK:
+ if (DBG) Log.d(TAG, "Unregister a service callback");
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
+ // If the binder death notification for a INsdManagerCallback was received
+ // before any calls are received by NsdService, the clientInfo would be
+ // cleared and cause NPE. Add a null check here to prevent this corner case.
+ if (clientInfo == null) {
+ Log.e(TAG, "Unknown connector in callback unregistration");
+ break;
+ }
+
+ id = clientInfo.mClientIds.get(clientId);
+ removeRequestMap(clientId, id, clientInfo);
+ if (stopResolveService(id)) {
+ clientInfo.onServiceInfoCallbackUnregistered(clientId);
+ } else {
+ Log.e(TAG, "Failed to unregister service info callback");
+ }
+ clearRegisteredServiceInfo(clientInfo);
+ break;
case MDNS_SERVICE_EVENT:
if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) {
return NOT_HANDLED;
@@ -670,6 +872,19 @@
return HANDLED;
}
+ private void notifyResolveFailedResult(boolean isListenedToUpdates, int clientId,
+ ClientInfo clientInfo, int error) {
+ if (isListenedToUpdates) {
+ clientInfo.onServiceInfoCallbackRegistrationFailed(clientId, error);
+ clearRegisteredServiceInfo(clientInfo);
+ } else {
+ // The resolve API always returned FAILURE_INTERNAL_ERROR on error; keep it
+ // for backwards compatibility.
+ clientInfo.onResolveServiceFailed(clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.mResolvedService = null;
+ }
+ }
+
private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
NsdServiceInfo servInfo;
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
@@ -704,6 +919,12 @@
// interfaces that do not have an associated Network.
break;
}
+ if (foundNetId == INetd.DUMMY_NET_ID) {
+ // Ignore services on the dummy0 interface: they are only seen when
+ // discovering locally advertised services, and are not reachable
+ // through that interface.
+ break;
+ }
setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx);
clientInfo.onServiceFound(clientId, servInfo);
break;
@@ -720,6 +941,8 @@
// found services on the same interface index and their network at the time
setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
clientInfo.onServiceLost(clientId, servInfo);
+ // TODO: also support registered service lost when not discovering
+ clientInfo.maybeNotifyRegisteredServiceLost(servInfo);
break;
}
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
@@ -756,10 +979,15 @@
String rest = fullName.substring(index);
String type = rest.replace(".local.", "");
- clientInfo.mResolvedService.setServiceName(name);
- clientInfo.mResolvedService.setServiceType(type);
- clientInfo.mResolvedService.setPort(info.port);
- clientInfo.mResolvedService.setTxtRecords(info.txtRecord);
+ final boolean isListenedToUpdates =
+ clientId == clientInfo.mClientIdForServiceUpdates;
+ final NsdServiceInfo serviceInfo = isListenedToUpdates
+ ? clientInfo.mRegisteredService : clientInfo.mResolvedService;
+
+ serviceInfo.setServiceName(name);
+ serviceInfo.setServiceType(type);
+ serviceInfo.setPort(info.port);
+ serviceInfo.setTxtRecords(info.txtRecord);
// Network will be added after SERVICE_GET_ADDR_SUCCESS
stopResolveService(id);
@@ -769,9 +997,8 @@
if (getAddrInfo(id2, info.hostname, info.interfaceIdx)) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
- clientInfo.onResolveServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
- clientInfo.mResolvedService = null;
+ notifyResolveFailedResult(isListenedToUpdates, clientId, clientInfo,
+ NsdManager.FAILURE_BAD_PARAMETERS);
}
break;
}
@@ -779,17 +1006,17 @@
/* NNN resolveId errorCode */
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
- clientInfo.onResolveServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ notifyResolveFailedResult(
+ clientId == clientInfo.mClientIdForServiceUpdates,
+ clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS);
break;
case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
- clientInfo.onResolveServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ notifyResolveFailedResult(
+ clientId == clientInfo.mClientIdForServiceUpdates,
+ clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS);
break;
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
/* NNN resolveId hostname ttl addr interfaceIdx netId */
@@ -806,19 +1033,38 @@
// If the resolved service is on an interface without a network, consider it
// as a failure: it would not be usable by apps as they would need
// privileged permissions.
- if (netId != NETID_UNSET && serviceHost != null) {
- clientInfo.mResolvedService.setHost(serviceHost);
- setServiceNetworkForCallback(clientInfo.mResolvedService,
- netId, info.interfaceIdx);
- clientInfo.onResolveServiceSucceeded(
- clientId, clientInfo.mResolvedService);
+ if (clientId == clientInfo.mClientIdForServiceUpdates) {
+ if (netId != NETID_UNSET && serviceHost != null) {
+ setServiceNetworkForCallback(clientInfo.mRegisteredService,
+ netId, info.interfaceIdx);
+ final List<InetAddress> addresses =
+ clientInfo.mRegisteredService.getHostAddresses();
+ addresses.add(serviceHost);
+ clientInfo.mRegisteredService.setHostAddresses(addresses);
+ clientInfo.onServiceUpdated(
+ clientId, clientInfo.mRegisteredService);
+ } else {
+ stopGetAddrInfo(id);
+ removeRequestMap(clientId, id, clientInfo);
+ clearRegisteredServiceInfo(clientInfo);
+ clientInfo.onServiceInfoCallbackRegistrationFailed(
+ clientId, NsdManager.FAILURE_BAD_PARAMETERS);
+ }
} else {
- clientInfo.onResolveServiceFailed(
- clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ if (netId != NETID_UNSET && serviceHost != null) {
+ clientInfo.mResolvedService.setHost(serviceHost);
+ setServiceNetworkForCallback(clientInfo.mResolvedService,
+ netId, info.interfaceIdx);
+ clientInfo.onResolveServiceSucceeded(
+ clientId, clientInfo.mResolvedService);
+ } else {
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
+ stopGetAddrInfo(id);
+ removeRequestMap(clientId, id, clientInfo);
+ clientInfo.mResolvedService = null;
}
- stopGetAddrInfo(id);
- removeRequestMap(clientId, id, clientInfo);
- clientInfo.mResolvedService = null;
break;
}
default:
@@ -863,6 +1109,43 @@
case NsdManager.SERVICE_LOST:
clientInfo.onServiceLost(clientId, info);
break;
+ case NsdManager.RESOLVE_SERVICE_SUCCEEDED: {
+ final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
+ // Add '.' in front of the service type that aligns with historical behavior
+ info.setServiceType("." + event.mRequestedServiceType);
+ info.setPort(serviceInfo.getPort());
+
+ Map<String, String> attrs = serviceInfo.getAttributes();
+ for (Map.Entry<String, String> kv : attrs.entrySet()) {
+ final String key = kv.getKey();
+ try {
+ info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid attribute", e);
+ }
+ }
+ try {
+ if (serviceInfo.getIpv4Address() != null) {
+ info.setHost(InetAddresses.parseNumericAddress(
+ serviceInfo.getIpv4Address()));
+ } else {
+ info.setHost(InetAddresses.parseNumericAddress(
+ serviceInfo.getIpv6Address()));
+ }
+ clientInfo.onResolveServiceSucceeded(clientId, info);
+ } catch (IllegalArgumentException e) {
+ Log.wtf(TAG, "Invalid address in RESOLVE_SERVICE_SUCCEEDED", e);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
+
+ // Unregister the listener immediately like IMDnsEventListener design
+ final MdnsListener listener = clientInfo.mListeners.get(clientId);
+ mMdnsDiscoveryManager.unregisterListener(
+ listener.getListenedServiceType(), listener);
+ removeListenerMap(clientId, transactionId, clientInfo);
+ break;
+ }
default:
return false;
}
@@ -933,18 +1216,32 @@
mNsdStateMachine.start();
mMDnsManager = ctx.getSystemService(MDnsManager.class);
mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
- if (deps.isMdnsDiscoveryManagerEnabled(ctx)) {
+
+ final boolean discoveryManagerEnabled = deps.isMdnsDiscoveryManagerEnabled(ctx);
+ final boolean advertiserEnabled = deps.isMdnsAdvertiserEnabled(ctx);
+ if (discoveryManagerEnabled || advertiserEnabled) {
mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
+ } else {
+ mMdnsSocketProvider = null;
+ }
+
+ if (discoveryManagerEnabled) {
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager =
deps.makeMdnsDiscoveryManager(new ExecutorProvider(), mMdnsSocketClient);
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
} else {
- mMdnsSocketProvider = null;
mMdnsSocketClient = null;
mMdnsDiscoveryManager = null;
}
+
+ if (advertiserEnabled) {
+ mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
+ new AdvertiserCallback());
+ } else {
+ mAdvertiser = null;
+ }
}
/**
@@ -953,10 +1250,10 @@
@VisibleForTesting
public static class Dependencies {
/**
- * Check whether or not MdnsDiscoveryManager feature is enabled.
+ * Check whether the MdnsDiscoveryManager feature is enabled.
*
* @param context The global context information about an app environment.
- * @return true if MdnsDiscoveryManager feature is enabled.
+ * @return true if the MdnsDiscoveryManager feature is enabled.
*/
public boolean isMdnsDiscoveryManagerEnabled(Context context) {
return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY,
@@ -964,6 +1261,17 @@
}
/**
+ * Check whether the MdnsAdvertiser feature is enabled.
+ *
+ * @param context The global context information about an app environment.
+ * @return true if the MdnsAdvertiser feature is enabled.
+ */
+ public boolean isMdnsAdvertiserEnabled(Context context) {
+ return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY,
+ MDNS_ADVERTISER_VERSION, false /* defaultEnabled */);
+ }
+
+ /**
* @see MdnsDiscoveryManager
*/
public MdnsDiscoveryManager makeMdnsDiscoveryManager(
@@ -972,6 +1280,15 @@
}
/**
+ * @see MdnsAdvertiser
+ */
+ public MdnsAdvertiser makeMdnsAdvertiser(
+ @NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
+ @NonNull MdnsAdvertiser.AdvertiserCallback cb) {
+ return new MdnsAdvertiser(looper, socketProvider, cb);
+ }
+
+ /**
* @see MdnsSocketProvider
*/
public MdnsSocketProvider makeMdnsSocketProvider(Context context, Looper looper) {
@@ -1029,6 +1346,49 @@
}
}
+ private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
+ @Override
+ public void onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo) {
+ final ClientInfo clientInfo = getClientInfoOrLog(serviceId);
+ if (clientInfo == null) return;
+
+ final int clientId = getClientIdOrLog(clientInfo, serviceId);
+ if (clientId < 0) return;
+
+ // onRegisterServiceSucceeded only has the service name in its info. This aligns with
+ // historical behavior.
+ final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
+ clientInfo.onRegisterServiceSucceeded(clientId, cbInfo);
+ }
+
+ @Override
+ public void onRegisterServiceFailed(int serviceId, int errorCode) {
+ final ClientInfo clientInfo = getClientInfoOrLog(serviceId);
+ if (clientInfo == null) return;
+
+ final int clientId = getClientIdOrLog(clientInfo, serviceId);
+ if (clientId < 0) return;
+
+ clientInfo.onRegisterServiceFailed(clientId, errorCode);
+ }
+
+ private ClientInfo getClientInfoOrLog(int serviceId) {
+ final ClientInfo clientInfo = mIdToClientInfoMap.get(serviceId);
+ if (clientInfo == null) {
+ Log.e(TAG, String.format("Callback for service %d has no client", serviceId));
+ }
+ return clientInfo;
+ }
+
+ private int getClientIdOrLog(@NonNull ClientInfo info, int serviceId) {
+ final int clientId = info.getClientId(serviceId);
+ if (clientId < 0) {
+ Log.e(TAG, String.format("Client ID not found for service %d", serviceId));
+ }
+ return clientId;
+ }
+ }
+
@Override
public INsdServiceConnector connect(INsdManagerCallback cb) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
@@ -1084,6 +1444,26 @@
}
@Override
+ public void stopResolution(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.STOP_RESOLUTION, 0, listenerKey, new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void registerServiceInfoCallback(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.REGISTER_SERVICE_CALLBACK, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void unregisterServiceInfoCallback(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.UNREGISTER_SERVICE_CALLBACK, 0, listenerKey,
+ new ListenerArgs(this, null)));
+ }
+
+ @Override
public void startDaemon() {
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
@@ -1244,6 +1624,11 @@
// The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsLegacy = false;
+ /*** The service that is registered to listen to its updates */
+ private NsdServiceInfo mRegisteredService;
+ /*** The client id that listen to updates */
+ private int mClientIdForServiceUpdates;
+
private ClientInfo(INsdManagerCallback cb) {
mCb = cb;
if (DBG) Log.d(TAG, "New client");
@@ -1292,7 +1677,11 @@
stopResolveService(globalId);
break;
case NsdManager.REGISTER_SERVICE:
- unregisterService(globalId);
+ if (mAdvertiser != null) {
+ mAdvertiser.removeService(globalId);
+ } else {
+ unregisterService(globalId);
+ }
break;
default:
break;
@@ -1321,6 +1710,18 @@
return mClientIds.keyAt(idx);
}
+ private void maybeNotifyRegisteredServiceLost(@NonNull NsdServiceInfo info) {
+ if (mRegisteredService == null) return;
+ if (!Objects.equals(mRegisteredService.getServiceName(), info.getServiceName())) return;
+ // Resolved services have a leading dot appended at the beginning of their type, but in
+ // discovered info it's at the end
+ if (!Objects.equals(
+ mRegisteredService.getServiceType() + ".", "." + info.getServiceType())) {
+ return;
+ }
+ onServiceUpdatedLost(mClientIdForServiceUpdates);
+ }
+
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
try {
mCb.onDiscoverServicesStarted(listenerKey, info);
@@ -1416,5 +1817,53 @@
Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
}
}
+
+ void onStopResolutionFailed(int listenerKey, int error) {
+ try {
+ mCb.onStopResolutionFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopResolutionFailed", e);
+ }
+ }
+
+ void onStopResolutionSucceeded(int listenerKey) {
+ try {
+ mCb.onStopResolutionSucceeded(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopResolutionSucceeded", e);
+ }
+ }
+
+ void onServiceInfoCallbackRegistrationFailed(int listenerKey, int error) {
+ try {
+ mCb.onServiceInfoCallbackRegistrationFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceInfoCallbackRegistrationFailed", e);
+ }
+ }
+
+ void onServiceUpdated(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onServiceUpdated(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceUpdated", e);
+ }
+ }
+
+ void onServiceUpdatedLost(int listenerKey) {
+ try {
+ mCb.onServiceUpdatedLost(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceUpdatedLost", e);
+ }
+ }
+
+ void onServiceInfoCallbackUnregistered(int listenerKey) {
+ try {
+ mCb.onServiceInfoCallbackUnregistered(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceInfoCallbackUnregistered", e);
+ }
+ }
}
}
diff --git a/service-t/src/com/android/server/mdns/ConnectivityMonitor.java b/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitor.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/ConnectivityMonitor.java
rename to service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitor.java
diff --git a/service-t/src/com/android/server/mdns/ConnectivityMonitorWithConnectivityManager.java b/service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/ConnectivityMonitorWithConnectivityManager.java
rename to service-t/src/com/android/server/connectivity/mdns/ConnectivityMonitorWithConnectivityManager.java
diff --git a/service-t/src/com/android/server/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/EnqueueMdnsQueryCallable.java
rename to service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
diff --git a/service-t/src/com/android/server/mdns/ExecutorProvider.java b/service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/ExecutorProvider.java
rename to service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java
diff --git a/service-t/src/com/android/server/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
similarity index 70%
rename from service-t/src/com/android/server/mdns/MdnsAdvertiser.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index 4e40efe..977478a 100644
--- a/service-t/src/com/android/server/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -29,10 +29,10 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.io.IOException;
import java.util.List;
import java.util.Map;
-import java.util.function.Predicate;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
/**
* MdnsAdvertiser manages advertising services per {@link com.android.server.NsdService} requests.
@@ -85,7 +85,7 @@
public void onRegisterServiceSucceeded(
@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
// Wait for all current interfaces to be done probing before notifying of success.
- if (anyAdvertiser(a -> a.isProbing(serviceId))) return;
+ if (any(mAllAdvertisers, (k, a) -> a.isProbing(serviceId))) return;
// The service may still be unregistered/renamed if a conflict is found on a later added
// interface, or if a conflicting announcement/reply is detected (RFC6762 9.)
@@ -102,7 +102,37 @@
@Override
public void onServiceConflict(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
- // TODO: handle conflicts found after registration (during or after probing)
+ if (DBG) {
+ Log.v(TAG, "Found conflict, restarted probing for service " + serviceId);
+ }
+
+ final Registration registration = mRegistrations.get(serviceId);
+ if (registration == null) return;
+ if (registration.mNotifiedRegistrationSuccess) {
+ // TODO: consider notifying clients that the service is no longer registered with
+ // the old name (back to probing). The legacy implementation did not send any
+ // callback though; it only sent onServiceRegistered after re-probing finishes
+ // (with the old, conflicting, actually not used name as argument... The new
+ // implementation will send callbacks with the new name).
+ registration.mNotifiedRegistrationSuccess = false;
+
+ // The service was done probing, just reset it to probing state (RFC6762 9.)
+ forAllAdvertisers(a -> a.restartProbingForConflict(serviceId));
+ return;
+ }
+
+ // Conflict was found during probing; rename once to find a name that has no conflict
+ registration.updateForConflict(
+ registration.makeNewServiceInfoForConflict(1 /* renameCount */),
+ 1 /* renameCount */);
+
+ // Keep renaming if the new name conflicts in local registrations
+ updateRegistrationUntilNoConflict((net, adv) -> adv.hasRegistration(registration),
+ registration);
+
+ // Update advertisers to use the new name
+ forAllAdvertisers(a -> a.renameServiceForConflict(
+ serviceId, registration.getServiceInfo()));
}
@Override
@@ -116,6 +146,25 @@
}
};
+ private boolean hasAnyConflict(
+ @NonNull BiPredicate<Network, InterfaceAdvertiserRequest> applicableAdvertiserFilter,
+ @NonNull NsdServiceInfo newInfo) {
+ return any(mAdvertiserRequests, (network, adv) ->
+ applicableAdvertiserFilter.test(network, adv) && adv.hasConflict(newInfo));
+ }
+
+ private void updateRegistrationUntilNoConflict(
+ @NonNull BiPredicate<Network, InterfaceAdvertiserRequest> applicableAdvertiserFilter,
+ @NonNull Registration registration) {
+ int renameCount = 0;
+ NsdServiceInfo newInfo = registration.getServiceInfo();
+ while (hasAnyConflict(applicableAdvertiserFilter, newInfo)) {
+ renameCount++;
+ newInfo = registration.makeNewServiceInfoForConflict(renameCount);
+ }
+ registration.updateForConflict(newInfo, renameCount);
+ }
+
/**
* A request for a {@link MdnsInterfaceAdvertiser}.
*
@@ -153,6 +202,21 @@
}
/**
+ * Return whether this {@link InterfaceAdvertiserRequest} has the given registration.
+ */
+ boolean hasRegistration(@NonNull Registration registration) {
+ return mPendingRegistrations.indexOfValue(registration) >= 0;
+ }
+
+ /**
+ * Return whether using the proposed new {@link NsdServiceInfo} to add a registration would
+ * cause a conflict in this {@link InterfaceAdvertiserRequest}.
+ */
+ boolean hasConflict(@NonNull NsdServiceInfo newInfo) {
+ return getConflictingService(newInfo) >= 0;
+ }
+
+ /**
* Get the ID of a conflicting service, or -1 if none.
*/
int getConflictingService(@NonNull NsdServiceInfo info) {
@@ -166,16 +230,19 @@
return -1;
}
- void addService(int id, Registration registration)
- throws NameConflictException {
- final int conflicting = getConflictingService(registration.getServiceInfo());
- if (conflicting >= 0) {
- throw new NameConflictException(conflicting);
- }
-
+ /**
+ * Add a service.
+ *
+ * Conflicts must be checked via {@link #getConflictingService} before attempting to add.
+ */
+ void addService(int id, Registration registration) {
mPendingRegistrations.put(id, registration);
for (int i = 0; i < mAdvertisers.size(); i++) {
- mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo());
+ try {
+ mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo());
+ } catch (NameConflictException e) {
+ Log.wtf(TAG, "Name conflict adding services that should have unique names", e);
+ }
}
}
@@ -239,32 +306,42 @@
/**
* Update the registration to use a different service name, after a conflict was found.
*
+ * @param newInfo New service info to use.
+ * @param renameCount How many renames were done before reaching the current name.
+ */
+ private void updateForConflict(@NonNull NsdServiceInfo newInfo, int renameCount) {
+ mConflictCount += renameCount;
+ mServiceInfo = newInfo;
+ }
+
+ /**
+ * Make a new service name for the registration, after a conflict was found.
+ *
* If a name conflict was found during probing or because different advertising requests
* used the same name, the registration is attempted again with a new name (here using
* a number suffix, (1), (2) etc). Registration success is notified once probing succeeds
* with a new name. This matches legacy behavior based on mdnsresponder, and appendix D of
* RFC6763.
- * @return The new service info with the updated name.
+ *
+ * @param renameCount How much to increase the number suffix for this conflict.
*/
@NonNull
- private NsdServiceInfo updateForConflict() {
- mConflictCount++;
+ public NsdServiceInfo makeNewServiceInfoForConflict(int renameCount) {
// In case of conflict choose a different service name. After the first conflict use
// "Name (2)", then "Name (3)" etc.
// TODO: use a hidden method in NsdServiceInfo once MdnsAdvertiser is moved to service-t
final NsdServiceInfo newInfo = new NsdServiceInfo();
- newInfo.setServiceName(mOriginalName + " (" + (mConflictCount + 1) + ")");
+ newInfo.setServiceName(mOriginalName + " (" + (mConflictCount + renameCount + 1) + ")");
newInfo.setServiceType(mServiceInfo.getServiceType());
for (Map.Entry<String, byte[]> attr : mServiceInfo.getAttributes().entrySet()) {
- newInfo.setAttribute(attr.getKey(), attr.getValue());
+ newInfo.setAttribute(attr.getKey(),
+ attr.getValue() == null ? null : new String(attr.getValue()));
}
newInfo.setHost(mServiceInfo.getHost());
newInfo.setPort(mServiceInfo.getPort());
newInfo.setNetwork(mServiceInfo.getNetwork());
// interfaceIndex is not set when registering
-
- mServiceInfo = newInfo;
- return mServiceInfo;
+ return newInfo;
}
@NonNull
@@ -338,55 +415,27 @@
Log.i(TAG, "Adding service " + service + " with ID " + id);
}
- try {
- final Registration registration = new Registration(service);
- while (!tryAddRegistration(id, registration)) {
- registration.updateForConflict();
- }
-
- mRegistrations.put(id, registration);
- } catch (IOException e) {
- Log.e(TAG, "Error adding service " + service, e);
- removeService(id);
- // TODO (b/264986328): add a more specific error code
- mCb.onRegisterServiceFailed(id, NsdManager.FAILURE_INTERNAL_ERROR);
- }
- }
-
- private boolean tryAddRegistration(int id, @NonNull Registration registration)
- throws IOException {
- final NsdServiceInfo serviceInfo = registration.getServiceInfo();
- final Network network = serviceInfo.getNetwork();
- try {
- InterfaceAdvertiserRequest advertiser = mAdvertiserRequests.get(network);
- if (advertiser == null) {
- advertiser = new InterfaceAdvertiserRequest(network);
- mAdvertiserRequests.put(network, advertiser);
- }
- advertiser.addService(id, registration);
- } catch (NameConflictException e) {
- if (DBG) {
- Log.i(TAG, "Service name conflicts: " + serviceInfo.getServiceName());
- }
- removeService(id);
- return false;
+ final Network network = service.getNetwork();
+ final Registration registration = new Registration(service);
+ final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
+ if (network == null) {
+ // If registering on all networks, no advertiser must have conflicts
+ checkConflictFilter = (net, adv) -> true;
+ } else {
+ // If registering on one network, the matching network advertiser and the one for all
+ // networks must not have conflicts
+ checkConflictFilter = (net, adv) -> net == null || network.equals(net);
}
- // When adding a service to a specific network, check that it does not conflict with other
- // registrations advertising on all networks
- final InterfaceAdvertiserRequest allNetworksAdvertiser = mAdvertiserRequests.get(null);
- if (network != null && allNetworksAdvertiser != null
- && allNetworksAdvertiser.getConflictingService(serviceInfo) >= 0) {
- if (DBG) {
- Log.i(TAG, "Service conflicts with advertisement on all networks: "
- + serviceInfo.getServiceName());
- }
- removeService(id);
- return false;
- }
+ updateRegistrationUntilNoConflict(checkConflictFilter, registration);
+ InterfaceAdvertiserRequest advertiser = mAdvertiserRequests.get(network);
+ if (advertiser == null) {
+ advertiser = new InterfaceAdvertiserRequest(network);
+ mAdvertiserRequests.put(network, advertiser);
+ }
+ advertiser.addService(id, registration);
mRegistrations.put(id, registration);
- return true;
}
/**
@@ -406,12 +455,20 @@
mRegistrations.remove(id);
}
- private boolean anyAdvertiser(@NonNull Predicate<MdnsInterfaceAdvertiser> predicate) {
- for (int i = 0; i < mAllAdvertisers.size(); i++) {
- if (predicate.test(mAllAdvertisers.valueAt(i))) {
+ private static <K, V> boolean any(@NonNull ArrayMap<K, V> map,
+ @NonNull BiPredicate<K, V> predicate) {
+ for (int i = 0; i < map.size(); i++) {
+ if (predicate.test(map.keyAt(i), map.valueAt(i))) {
return true;
}
}
return false;
}
+
+ private void forAllAdvertisers(@NonNull Consumer<MdnsInterfaceAdvertiser> consumer) {
+ any(mAllAdvertisers, (socket, advertiser) -> {
+ consumer.accept(advertiser);
+ return false;
+ });
+ }
}
diff --git a/service-t/src/com/android/server/mdns/MdnsAnnouncer.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsAnnouncer.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsAnnouncer.java
diff --git a/service-t/src/com/android/server/mdns/MdnsAnyRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAnyRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsAnyRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsAnyRecord.java
diff --git a/service-t/src/com/android/server/mdns/MdnsConfigs.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsConfigs.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
diff --git a/service-t/src/com/android/server/mdns/MdnsConstants.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
similarity index 97%
rename from service-t/src/com/android/server/mdns/MdnsConstants.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
index 396be5f..f0e1717 100644
--- a/service-t/src/com/android/server/mdns/MdnsConstants.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
@@ -37,6 +37,7 @@
public static final int FLAGS_QUERY = 0x0000;
public static final int FLAGS_RESPONSE_MASK = 0xF80F;
public static final int FLAGS_RESPONSE = 0x8000;
+ public static final int FLAG_TRUNCATED = 0x0200;
public static final int QCLASS_INTERNET = 0x0001;
public static final int QCLASS_UNICAST = 0x8000;
public static final String SUBTYPE_LABEL = "_sub";
diff --git a/service-t/src/com/android/server/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsDiscoveryManager.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
diff --git a/service-t/src/com/android/server/mdns/MdnsInetAddressRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsInetAddressRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsInetAddressRecord.java
diff --git a/service-t/src/com/android/server/mdns/MdnsInterfaceAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
similarity index 80%
rename from service-t/src/com/android/server/mdns/MdnsInterfaceAdvertiser.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 790e69a..c616e01 100644
--- a/service-t/src/com/android/server/mdns/MdnsInterfaceAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -25,16 +25,18 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.HexDump;
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo;
import com.android.server.connectivity.mdns.MdnsPacketRepeater.PacketRepeaterCallback;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.List;
/**
* A class that handles advertising services on a {@link MdnsInterfaceSocket} tied to an interface.
*/
-public class MdnsInterfaceAdvertiser {
+public class MdnsInterfaceAdvertiser implements MulticastPacketReader.PacketHandler {
private static final boolean DBG = MdnsAdvertiser.DBG;
@VisibleForTesting
public static final long EXIT_ANNOUNCEMENT_DELAY_MS = 100L;
@@ -145,9 +147,9 @@
/** @see MdnsReplySender */
@NonNull
- public MdnsReplySender makeReplySender(@NonNull Looper looper,
+ public MdnsReplySender makeReplySender(@NonNull String interfaceTag, @NonNull Looper looper,
@NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer) {
- return new MdnsReplySender(looper, socket, packetCreationBuffer);
+ return new MdnsReplySender(interfaceTag, looper, socket, packetCreationBuffer);
}
/** @see MdnsAnnouncer */
@@ -182,7 +184,7 @@
mSocket = socket;
mCb = cb;
mCbHandler = new Handler(looper);
- mReplySender = deps.makeReplySender(looper, socket, packetCreationBuffer);
+ mReplySender = deps.makeReplySender(logTag, looper, socket, packetCreationBuffer);
mAnnouncer = deps.makeMdnsAnnouncer(logTag, looper, mReplySender,
mAnnouncingCallback);
mProber = deps.makeMdnsProber(logTag, looper, mReplySender, mProbingCallback);
@@ -196,7 +198,7 @@
* {@link #destroyNow()}.
*/
public void start() {
- // TODO: start receiving packets
+ mSocket.addPacketHandler(this);
}
/**
@@ -267,8 +269,8 @@
mProber.stop(serviceId);
mAnnouncer.stop(serviceId);
}
-
- // TODO: stop receiving packets
+ mReplySender.cancelAll();
+ mSocket.removePacketHandler(this);
mCbHandler.post(() -> mCb.onDestroyed(mSocket));
}
@@ -276,14 +278,23 @@
* Reset a service to the probing state due to a conflict found on the network.
*/
public void restartProbingForConflict(int serviceId) {
- // TODO: implement
+ final MdnsProber.ProbingInfo probingInfo = mRecordRepository.setServiceProbing(serviceId);
+ if (probingInfo == null) return;
+
+ mProber.restartForConflict(probingInfo);
}
/**
* Rename a service following a conflict found on the network, and restart probing.
+ *
+ * If the service was not registered on this {@link MdnsInterfaceAdvertiser}, this is a no-op.
*/
public void renameServiceForConflict(int serviceId, NsdServiceInfo newInfo) {
- // TODO: implement
+ final MdnsProber.ProbingInfo probingInfo = mRecordRepository.renameServiceForConflict(
+ serviceId, newInfo);
+ if (probingInfo == null) return;
+
+ mProber.restartForConflict(probingInfo);
}
/**
@@ -294,4 +305,40 @@
public boolean isProbing(int serviceId) {
return mRecordRepository.isProbing(serviceId);
}
+
+ @Override
+ public void handlePacket(byte[] recvbuf, int length, InetSocketAddress src) {
+ final MdnsPacket packet;
+ try {
+ packet = MdnsPacket.parse(new MdnsPacketReader(recvbuf, length));
+ } catch (MdnsPacket.ParseException e) {
+ Log.e(mTag, "Error parsing mDNS packet", e);
+ if (DBG) {
+ Log.v(
+ mTag, "Packet: " + HexDump.toHexString(recvbuf, 0, length));
+ }
+ return;
+ }
+
+ if (DBG) {
+ Log.v(mTag,
+ "Parsed packet with " + packet.questions.size() + " questions, "
+ + packet.answers.size() + " answers, "
+ + packet.authorityRecords.size() + " authority, "
+ + packet.additionalRecords.size() + " additional from " + src);
+ }
+
+ for (int conflictServiceId : mRecordRepository.getConflictingServices(packet)) {
+ mCbHandler.post(() -> mCb.onServiceConflict(this, conflictServiceId));
+ }
+
+ // Even in case of conflict, add replies for other services. But in general conflicts would
+ // happen when the incoming packet has answer records (not a question), so there will be no
+ // answer. One exception is simultaneous probe tiebreaking (rfc6762 8.2), in which case the
+ // conflicting service is still probing and won't reply either.
+ final MdnsRecordRepository.ReplyInfo answers = mRecordRepository.getReply(packet, src);
+
+ if (answers == null) return;
+ mReplySender.queueReply(answers);
+ }
}
diff --git a/service-t/src/com/android/server/mdns/MdnsInterfaceSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
similarity index 95%
rename from service-t/src/com/android/server/mdns/MdnsInterfaceSocket.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
index d1290b6..119c7a8 100644
--- a/service-t/src/com/android/server/mdns/MdnsInterfaceSocket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
@@ -162,6 +162,14 @@
}
/**
+ * Remove a handler added via {@link #addPacketHandler}. If the handler is not present, this is
+ * a no-op.
+ */
+ public void removePacketHandler(@NonNull MulticastPacketReader.PacketHandler handler) {
+ mPacketReader.removePacketHandler(handler);
+ }
+
+ /**
* Returns the network interface that this socket is bound to.
*
* <p>This method could be used on any thread.
diff --git a/service-t/src/com/android/server/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsMultinetworkSocketClient.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
diff --git a/service-t/src/com/android/server/mdns/MdnsNsecRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsNsecRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
new file mode 100644
index 0000000..27002b9
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacket.java
@@ -0,0 +1,231 @@
+/*
+ * 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.util.Log;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class holding data that can be included in a mDNS packet.
+ */
+public class MdnsPacket {
+ private static final String TAG = MdnsPacket.class.getSimpleName();
+
+ public final int flags;
+ @NonNull
+ public final List<MdnsRecord> questions;
+ @NonNull
+ public final List<MdnsRecord> answers;
+ @NonNull
+ public final List<MdnsRecord> authorityRecords;
+ @NonNull
+ public final List<MdnsRecord> additionalRecords;
+
+ MdnsPacket(int flags,
+ @NonNull List<MdnsRecord> questions,
+ @NonNull List<MdnsRecord> answers,
+ @NonNull List<MdnsRecord> authorityRecords,
+ @NonNull 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);
+ }
+
+ /**
+ * Exception thrown on parse errors.
+ */
+ public static class ParseException extends IOException {
+ public final int code;
+
+ public ParseException(int code, @NonNull String message, @Nullable Throwable cause) {
+ super(message, cause);
+ this.code = code;
+ }
+ }
+
+ /**
+ * Parse the packet in the provided {@link MdnsPacketReader}.
+ */
+ @NonNull
+ public static MdnsPacket parse(@NonNull MdnsPacketReader reader) throws ParseException {
+ final int flags;
+ try {
+ reader.readUInt16(); // transaction ID (not used)
+ flags = reader.readUInt16();
+ } catch (EOFException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_END_OF_FILE,
+ "Reached the end of the mDNS response unexpectedly.", e);
+ }
+ return parseRecordsSection(reader, flags);
+ }
+
+ /**
+ * Parse the records section of a mDNS packet in the provided {@link MdnsPacketReader}.
+ *
+ * The records section starts with the questions count, just after the packet flags.
+ */
+ public static MdnsPacket parseRecordsSection(@NonNull MdnsPacketReader reader, int flags)
+ throws ParseException {
+ try {
+ final int numQuestions = reader.readUInt16();
+ final int numAnswers = reader.readUInt16();
+ final int numAuthority = reader.readUInt16();
+ final int numAdditional = reader.readUInt16();
+
+ final ArrayList<MdnsRecord> questions = parseRecords(reader, numQuestions, true);
+ final ArrayList<MdnsRecord> answers = parseRecords(reader, numAnswers, false);
+ final ArrayList<MdnsRecord> authority = parseRecords(reader, numAuthority, false);
+ final ArrayList<MdnsRecord> additional = parseRecords(reader, numAdditional, false);
+
+ return new MdnsPacket(flags, questions, answers, authority, additional);
+ } catch (EOFException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_END_OF_FILE,
+ "Reached the end of the mDNS response unexpectedly.", e);
+ }
+ }
+
+ private static ArrayList<MdnsRecord> parseRecords(@NonNull MdnsPacketReader reader, int count,
+ boolean isQuestion)
+ throws ParseException {
+ final ArrayList<MdnsRecord> records = new ArrayList<>(count);
+ for (int i = 0; i < count; ++i) {
+ final MdnsRecord record = parseRecord(reader, isQuestion);
+ if (record != null) {
+ records.add(record);
+ }
+ }
+ return records;
+ }
+
+ @Nullable
+ private static MdnsRecord parseRecord(@NonNull MdnsPacketReader reader, boolean isQuestion)
+ throws ParseException {
+ String[] name;
+ try {
+ name = reader.readLabels();
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_RECORD_NAME,
+ "Failed to read labels from mDNS response.", e);
+ }
+
+ final int type;
+ try {
+ type = reader.readUInt16();
+ } catch (EOFException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_END_OF_FILE,
+ "Reached the end of the mDNS response unexpectedly.", e);
+ }
+
+ switch (type) {
+ case MdnsRecord.TYPE_A: {
+ try {
+ return new MdnsInetAddressRecord(name, MdnsRecord.TYPE_A, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_A_RDATA,
+ "Failed to read A record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_AAAA: {
+ try {
+ return new MdnsInetAddressRecord(name,
+ MdnsRecord.TYPE_AAAA, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_AAAA_RDATA,
+ "Failed to read AAAA record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_PTR: {
+ try {
+ return new MdnsPointerRecord(name, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_PTR_RDATA,
+ "Failed to read PTR record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_SRV: {
+ try {
+ return new MdnsServiceRecord(name, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_SRV_RDATA,
+ "Failed to read SRV record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_TXT: {
+ try {
+ return new MdnsTextRecord(name, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_TXT_RDATA,
+ "Failed to read TXT record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_NSEC: {
+ try {
+ return new MdnsNsecRecord(name, reader, isQuestion);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_NSEC_RDATA,
+ "Failed to read NSEC record from mDNS response.", e);
+ }
+ }
+
+ case MdnsRecord.TYPE_ANY: {
+ try {
+ return new MdnsAnyRecord(name, reader);
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_READING_ANY_RDATA,
+ "Failed to read TYPE_ANY record from mDNS response.", e);
+ }
+ }
+
+ default: {
+ try {
+ if (MdnsAdvertiser.DBG) {
+ Log.i(TAG, "Skipping parsing of record of unhandled type " + type);
+ }
+ skipMdnsRecord(reader, isQuestion);
+ return null;
+ } catch (IOException e) {
+ throw new ParseException(MdnsResponseErrorCode.ERROR_SKIPPING_UNKNOWN_RECORD,
+ "Failed to skip mDNS record.", e);
+ }
+ }
+ }
+ }
+
+ private static void skipMdnsRecord(@NonNull MdnsPacketReader reader, boolean isQuestion)
+ throws IOException {
+ reader.skip(2); // Skip the class
+ if (isQuestion) return;
+ // Skip TTL and data
+ reader.skip(4);
+ int dataLength = reader.readUInt16();
+ reader.skip(dataLength);
+ }
+}
diff --git a/service-t/src/com/android/server/mdns/MdnsPacketReader.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketReader.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsPacketReader.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsPacketReader.java
diff --git a/service-t/src/com/android/server/mdns/MdnsPacketRepeater.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
similarity index 94%
rename from service-t/src/com/android/server/mdns/MdnsPacketRepeater.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
index ae54e70..4c385da 100644
--- a/service-t/src/com/android/server/mdns/MdnsPacketRepeater.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketRepeater.java
@@ -16,6 +16,9 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsRecordRepository.IPV4_ADDR;
+import static com.android.server.connectivity.mdns.MdnsRecordRepository.IPV6_ADDR;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
@@ -32,10 +35,6 @@
*/
public abstract class MdnsPacketRepeater<T extends MdnsPacketRepeater.Request> {
private static final boolean DBG = MdnsAdvertiser.DBG;
- private static final InetSocketAddress IPV4_ADDR = new InetSocketAddress(
- MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
- private static final InetSocketAddress IPV6_ADDR = new InetSocketAddress(
- MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
private static final InetSocketAddress[] ALL_ADDRS = new InetSocketAddress[] {
IPV4_ADDR, IPV6_ADDR
};
@@ -114,7 +113,7 @@
final MdnsPacket packet = request.getPacket(index);
if (DBG) {
Log.v(getTag(), "Sending packets for iteration " + index + " out of "
- + request.getNumSends());
+ + request.getNumSends() + " for ID " + msg.what);
}
// Send to both v4 and v6 addresses; the reply sender will take care of ignoring the
// send when the socket has not joined the relevant group.
diff --git a/service-t/src/com/android/server/mdns/MdnsPacketWriter.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsPacketWriter.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
diff --git a/service-t/src/com/android/server/mdns/MdnsPointerRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsPointerRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
diff --git a/service-t/src/com/android/server/mdns/MdnsProber.java b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
similarity index 86%
rename from service-t/src/com/android/server/mdns/MdnsProber.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
index 2cd9148..669b323 100644
--- a/service-t/src/com/android/server/mdns/MdnsProber.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
@@ -33,13 +33,13 @@
* TODO: implement receiving replies and handling conflicts.
*/
public class MdnsProber extends MdnsPacketRepeater<MdnsProber.ProbingInfo> {
+ private static final long CONFLICT_RETRY_DELAY_MS = 5_000L;
@NonNull
private final String mLogTag;
public MdnsProber(@NonNull String interfaceTag, @NonNull Looper looper,
@NonNull MdnsReplySender replySender,
@NonNull PacketRepeaterCallback<ProbingInfo> cb) {
- // 3 packets as per https://datatracker.ietf.org/doc/html/rfc6762#section-8.1
super(looper, replySender, cb);
mLogTag = MdnsProber.class.getSimpleName() + "/" + interfaceTag;
}
@@ -140,4 +140,18 @@
private void startProbing(@NonNull ProbingInfo info, long delay) {
startSending(info.getServiceId(), info, delay);
}
+
+ /**
+ * Restart probing with new service info as a conflict was found.
+ */
+ public void restartForConflict(@NonNull ProbingInfo newInfo) {
+ stop(newInfo.getServiceId());
+
+ /* RFC 6762 8.1: "If fifteen conflicts occur within any ten-second period, then the host
+ MUST wait at least five seconds before each successive additional probe attempt. [...]
+ For very simple devices, a valid way to comply with this requirement is to always wait
+ five seconds after any failed probe attempt before trying again. */
+ // TODO: count 15 conflicts in 10s instead of waiting for 5s every time
+ startProbing(newInfo, CONFLICT_RETRY_DELAY_MS);
+ }
}
diff --git a/service-t/src/com/android/server/mdns/MdnsRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
similarity index 99%
rename from service-t/src/com/android/server/mdns/MdnsRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
index 00871ea..bcee9d1 100644
--- a/service-t/src/com/android/server/mdns/MdnsRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -45,6 +45,7 @@
private static final int FLAG_CACHE_FLUSH = 0x8000;
public static final long RECEIPT_TIME_NOT_SENT = 0L;
+ public static final int CLASS_ANY = 0x00ff;
/** Status indicating that the record is current. */
public static final int STATUS_OK = 0;
@@ -317,4 +318,4 @@
return (recordType * 31) + Arrays.hashCode(recordName);
}
}
-}
\ No newline at end of file
+}
diff --git a/service-t/src/com/android/server/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
similarity index 65%
rename from service-t/src/com/android/server/mdns/MdnsRecordRepository.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index dd00212..e975ab4 100644
--- a/service-t/src/com/android/server/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Arrays;
@@ -42,6 +43,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
@@ -54,6 +57,9 @@
*/
@TargetApi(Build.VERSION_CODES.TIRAMISU) // Allow calling T+ APIs; this is only loaded on T+
public class MdnsRecordRepository {
+ // RFC6762 p.15
+ private static final long MIN_MULTICAST_REPLY_INTERVAL_MS = 1_000L;
+
// TTLs as per RFC6762 10.
// TTL for records with a host name as the resource record's name (e.g., A, AAAA, HINFO) or a
// host name contained within the resource record's rdata (e.g., SRV, reverse mapping PTR
@@ -69,6 +75,13 @@
private static final String[] DNS_SD_SERVICE_TYPE =
new String[] { "_services", "_dns-sd", "_udp", LOCAL_TLD };
+ public static final InetSocketAddress IPV6_ADDR = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
+ public static final InetSocketAddress IPV4_ADDR = new InetSocketAddress(
+ MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
+
+ @NonNull
+ private final Random mDelayGenerator = new Random();
// Map of service unique ID -> records for service
@NonNull
private final SparseArray<ServiceRegistration> mServices = new SparseArray<>();
@@ -139,6 +152,11 @@
public boolean isProbing;
/**
+ * Last time (as per SystemClock.elapsedRealtime) when advertised via multicast, 0 if never
+ */
+ public long lastAdvertisedTimeMs;
+
+ /**
* Last time (as per SystemClock.elapsedRealtime) when sent via unicast or multicast,
* 0 if never
*/
@@ -391,6 +409,212 @@
}
/**
+ * Info about a reply to be sent.
+ */
+ public static class ReplyInfo {
+ @NonNull
+ public final List<MdnsRecord> answers;
+ @NonNull
+ public final List<MdnsRecord> additionalAnswers;
+ public final long sendDelayMs;
+ @NonNull
+ public final InetSocketAddress destination;
+
+ public ReplyInfo(
+ @NonNull List<MdnsRecord> answers,
+ @NonNull List<MdnsRecord> additionalAnswers,
+ long sendDelayMs,
+ @NonNull InetSocketAddress destination) {
+ this.answers = answers;
+ this.additionalAnswers = additionalAnswers;
+ this.sendDelayMs = sendDelayMs;
+ this.destination = destination;
+ }
+
+ @Override
+ public String toString() {
+ return "{ReplyInfo to " + destination + ", answers: " + answers.size()
+ + ", additionalAnswers: " + additionalAnswers.size()
+ + ", sendDelayMs " + sendDelayMs + "}";
+ }
+ }
+
+ /**
+ * Get the reply to send to an incoming packet.
+ *
+ * @param packet The incoming packet.
+ * @param src The source address of the incoming packet.
+ */
+ @Nullable
+ public ReplyInfo getReply(MdnsPacket packet, InetSocketAddress src) {
+ final long now = SystemClock.elapsedRealtime();
+ final boolean replyUnicast = (packet.flags & MdnsConstants.QCLASS_UNICAST) != 0;
+ final ArrayList<MdnsRecord> additionalAnswerRecords = new ArrayList<>();
+ final ArrayList<RecordInfo<?>> answerInfo = new ArrayList<>();
+ for (MdnsRecord question : packet.questions) {
+ // Add answers from general records
+ addReplyFromService(question, mGeneralRecords, null /* servicePtrRecord */,
+ null /* serviceSrvRecord */, null /* serviceTxtRecord */, replyUnicast, now,
+ answerInfo, additionalAnswerRecords);
+
+ // Add answers from each service
+ for (int i = 0; i < mServices.size(); i++) {
+ final ServiceRegistration registration = mServices.valueAt(i);
+ if (registration.exiting) continue;
+ addReplyFromService(question, registration.allRecords, registration.ptrRecord,
+ registration.srvRecord, registration.txtRecord, replyUnicast, now,
+ answerInfo, additionalAnswerRecords);
+ }
+ }
+
+ if (answerInfo.size() == 0 && additionalAnswerRecords.size() == 0) {
+ return null;
+ }
+
+ // Determine the send delay
+ final long delayMs;
+ if ((packet.flags & MdnsConstants.FLAG_TRUNCATED) != 0) {
+ // RFC 6762 6.: 400-500ms delay if TC bit is set
+ delayMs = 400L + mDelayGenerator.nextInt(100);
+ } else if (packet.questions.size() > 1
+ || CollectionUtils.any(answerInfo, a -> a.isSharedName)) {
+ // 20-120ms if there may be responses from other hosts (not a fully owned
+ // name) (RFC 6762 6.), or if there are multiple questions (6.3).
+ // TODO: this should be 0 if this is a probe query ("can be distinguished from a
+ // normal query by the fact that a probe query contains a proposed record in the
+ // Authority Section that answers the question" in 6.), and the reply is for a fully
+ // owned record.
+ delayMs = 20L + mDelayGenerator.nextInt(100);
+ } else {
+ delayMs = 0L;
+ }
+
+ // Determine the send destination
+ final InetSocketAddress dest;
+ if (replyUnicast) {
+ dest = src;
+ } else if (src.getAddress() instanceof Inet4Address) {
+ dest = IPV4_ADDR;
+ } else {
+ dest = IPV6_ADDR;
+ }
+
+ // Build the list of answer records from their RecordInfo
+ final ArrayList<MdnsRecord> answerRecords = new ArrayList<>(answerInfo.size());
+ for (RecordInfo<?> info : answerInfo) {
+ // TODO: consider actual packet send delay after response aggregation
+ info.lastSentTimeMs = now + delayMs;
+ if (!replyUnicast) {
+ info.lastAdvertisedTimeMs = info.lastSentTimeMs;
+ }
+ answerRecords.add(info.record);
+ }
+
+ return new ReplyInfo(answerRecords, additionalAnswerRecords, delayMs, dest);
+ }
+
+ /**
+ * Add answers and additional answers for a question, from a ServiceRegistration.
+ */
+ private void addReplyFromService(@NonNull MdnsRecord question,
+ @NonNull List<RecordInfo<?>> serviceRecords,
+ @Nullable RecordInfo<MdnsPointerRecord> servicePtrRecord,
+ @Nullable RecordInfo<MdnsServiceRecord> serviceSrvRecord,
+ @Nullable RecordInfo<MdnsTextRecord> serviceTxtRecord,
+ boolean replyUnicast, long now, @NonNull List<RecordInfo<?>> answerInfo,
+ @NonNull List<MdnsRecord> additionalAnswerRecords) {
+ boolean hasDnsSdPtrRecordAnswer = false;
+ boolean hasDnsSdSrvRecordAnswer = false;
+ boolean hasFullyOwnedNameMatch = false;
+ boolean hasKnownAnswer = false;
+
+ final int answersStartIndex = answerInfo.size();
+ for (RecordInfo<?> info : serviceRecords) {
+ if (info.isProbing) continue;
+
+ /* RFC6762 6.: the record name must match the question name, the record rrtype
+ must match the question qtype unless the qtype is "ANY" (255) or the rrtype is
+ "CNAME" (5), and the record rrclass must match the question qclass unless the
+ qclass is "ANY" (255) */
+ if (!Arrays.equals(info.record.getName(), question.getName())) continue;
+ hasFullyOwnedNameMatch |= !info.isSharedName;
+
+ // The repository does not store CNAME records
+ if (question.getType() != MdnsRecord.TYPE_ANY
+ && question.getType() != info.record.getType()) {
+ continue;
+ }
+ if (question.getRecordClass() != MdnsRecord.CLASS_ANY
+ && question.getRecordClass() != info.record.getRecordClass()) {
+ continue;
+ }
+
+ hasKnownAnswer = true;
+ hasDnsSdPtrRecordAnswer |= (info == servicePtrRecord);
+ hasDnsSdSrvRecordAnswer |= (info == serviceSrvRecord);
+
+ // TODO: responses to probe queries should bypass this check and only ensure the
+ // reply is sent 250ms after the last sent time (RFC 6762 p.15)
+ if (!replyUnicast && info.lastAdvertisedTimeMs > 0L
+ && now - info.lastAdvertisedTimeMs < MIN_MULTICAST_REPLY_INTERVAL_MS) {
+ continue;
+ }
+
+ // TODO: Don't reply if in known answers of the querier (7.1) if TTL is > half
+
+ answerInfo.add(info);
+ }
+
+ // RFC6762 6.1:
+ // "Any time a responder receives a query for a name for which it has verified exclusive
+ // ownership, for a type for which that name has no records, the responder MUST [...]
+ // respond asserting the nonexistence of that record"
+ if (hasFullyOwnedNameMatch && !hasKnownAnswer) {
+ additionalAnswerRecords.add(new MdnsNsecRecord(
+ question.getName(),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ // TODO: RFC6762 6.1: "In general, the TTL given for an NSEC record SHOULD
+ // be the same as the TTL that the record would have had, had it existed."
+ NAME_RECORDS_TTL_MILLIS,
+ question.getName(),
+ new int[] { question.getType() }));
+ }
+
+ // No more records to add if no answer
+ if (answerInfo.size() == answersStartIndex) return;
+
+ final List<RecordInfo<?>> additionalAnswerInfo = new ArrayList<>();
+ // RFC6763 12.1: if including PTR record, include the SRV and TXT records it names
+ if (hasDnsSdPtrRecordAnswer) {
+ if (serviceTxtRecord != null) {
+ additionalAnswerInfo.add(serviceTxtRecord);
+ }
+ if (serviceSrvRecord != null) {
+ additionalAnswerInfo.add(serviceSrvRecord);
+ }
+ }
+
+ // RFC6763 12.1&.2: if including PTR or SRV record, include the address records it names
+ if (hasDnsSdPtrRecordAnswer || hasDnsSdSrvRecordAnswer) {
+ for (RecordInfo<?> record : mGeneralRecords) {
+ if (record.record instanceof MdnsInetAddressRecord) {
+ additionalAnswerInfo.add(record);
+ }
+ }
+ }
+
+ for (RecordInfo<?> info : additionalAnswerInfo) {
+ additionalAnswerRecords.add(info.record);
+ }
+
+ // RFC6762 6.1: negative responses
+ addNsecRecordsForUniqueNames(additionalAnswerRecords,
+ answerInfo.listIterator(answersStartIndex),
+ additionalAnswerInfo.listIterator());
+ }
+
+ /**
* Add NSEC records indicating that the response records are unique.
*
* Following RFC6762 6.1:
@@ -498,6 +722,55 @@
}
/**
+ * Get the service IDs of services conflicting with a received packet.
+ */
+ public Set<Integer> getConflictingServices(MdnsPacket packet) {
+ // Avoid allocating a new set for each incoming packet: use an empty set by default.
+ Set<Integer> conflicting = Collections.emptySet();
+ for (MdnsRecord record : packet.answers) {
+ for (int i = 0; i < mServices.size(); i++) {
+ final ServiceRegistration registration = mServices.valueAt(i);
+ if (registration.exiting) continue;
+
+ // Only look for conflicts in service name, as a different service name can be used
+ // if there is a conflict, but there is nothing actionable if any other conflict
+ // happens. In fact probing is only done for the service name in the SRV record.
+ // This means only SRV and TXT records need to be checked.
+ final RecordInfo<MdnsServiceRecord> srvRecord = registration.srvRecord;
+ if (!Arrays.equals(record.getName(), srvRecord.record.getName())) continue;
+
+ // As per RFC6762 9., it's fine if the "conflict" is an identical record with same
+ // data.
+ if (record instanceof MdnsServiceRecord) {
+ final MdnsServiceRecord local = srvRecord.record;
+ final MdnsServiceRecord other = (MdnsServiceRecord) record;
+ // Note "equals" does not consider TTL or receipt time, as intended here
+ if (Objects.equals(local, other)) {
+ continue;
+ }
+ }
+
+ if (record instanceof MdnsTextRecord) {
+ final MdnsTextRecord local = registration.txtRecord.record;
+ final MdnsTextRecord other = (MdnsTextRecord) record;
+ if (Objects.equals(local, other)) {
+ continue;
+ }
+ }
+
+ if (conflicting.size() == 0) {
+ // Conflict was found: use a mutable set
+ conflicting = new ArraySet<>();
+ }
+ final int serviceId = mServices.keyAt(i);
+ conflicting.add(serviceId);
+ }
+ }
+
+ return conflicting;
+ }
+
+ /**
* (Re)set a service to the probing state.
* @return The {@link MdnsProber.ProbingInfo} to send for probing.
*/
@@ -531,6 +804,21 @@
}
/**
+ * Rename a service to the newly provided info, following a conflict.
+ *
+ * If the specified service does not exist, this returns null.
+ */
+ @Nullable
+ public MdnsProber.ProbingInfo renameServiceForConflict(int serviceId, NsdServiceInfo newInfo) {
+ if (!mServices.contains(serviceId)) return null;
+
+ final ServiceRegistration newService = new ServiceRegistration(
+ mDeviceHostname, newInfo);
+ mServices.put(serviceId, newService);
+ return makeProbingInfo(serviceId, newService.srvRecord.record);
+ }
+
+ /**
* Called when {@link MdnsAdvertiser} sent an advertisement for the given service.
*/
public void onAdvertisementSent(int serviceId) {
@@ -540,6 +828,7 @@
final long now = SystemClock.elapsedRealtime();
for (RecordInfo<?> record : registration.allRecords) {
record.lastSentTimeMs = now;
+ record.lastAdvertisedTimeMs = now;
}
}
diff --git a/service-t/src/com/android/server/mdns/MdnsReplySender.java b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
similarity index 61%
rename from service-t/src/com/android/server/mdns/MdnsReplySender.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
index c6b8f47..f1389ca 100644
--- a/service-t/src/com/android/server/mdns/MdnsReplySender.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -16,8 +16,15 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
+
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.server.connectivity.mdns.MdnsRecordRepository.ReplyInfo;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -25,6 +32,7 @@
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
+import java.util.Collections;
/**
* A class that handles sending mDNS replies to a {@link MulticastSocket}, possibly queueing them
@@ -33,30 +41,46 @@
* TODO: implement sending after a delay, combining queued replies and duplicate answer suppression
*/
public class MdnsReplySender {
+ private static final boolean DBG = MdnsAdvertiser.DBG;
+ private static final int MSG_SEND = 1;
+
+ private final String mLogTag;
@NonNull
private final MdnsInterfaceSocket mSocket;
@NonNull
- private final Looper mLooper;
+ private final Handler mHandler;
@NonNull
private final byte[] mPacketCreationBuffer;
- public MdnsReplySender(@NonNull Looper looper,
+ public MdnsReplySender(@NonNull String interfaceTag, @NonNull Looper looper,
@NonNull MdnsInterfaceSocket socket, @NonNull byte[] packetCreationBuffer) {
- mLooper = looper;
+ mHandler = new SendHandler(looper);
+ mLogTag = MdnsReplySender.class.getSimpleName() + "/" + interfaceTag;
mSocket = socket;
mPacketCreationBuffer = packetCreationBuffer;
}
/**
+ * Queue a reply to be sent when its send delay expires.
+ */
+ public void queueReply(@NonNull ReplyInfo reply) {
+ ensureRunningOnHandlerThread(mHandler);
+ // TODO: implement response aggregation (RFC 6762 6.4)
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SEND, reply), reply.sendDelayMs);
+
+ if (DBG) {
+ Log.v(mLogTag, "Scheduling " + reply);
+ }
+ }
+
+ /**
* Send a packet immediately.
*
* Must be called on the looper thread used by the {@link MdnsReplySender}.
*/
public void sendNow(@NonNull MdnsPacket packet, @NonNull InetSocketAddress destination)
throws IOException {
- if (Thread.currentThread() != mLooper.getThread()) {
- throw new IllegalStateException("sendNow must be called in the handler thread");
- }
+ ensureRunningOnHandlerThread(mHandler);
if (!((destination.getAddress() instanceof Inet6Address && mSocket.hasJoinedIpv6())
|| (destination.getAddress() instanceof Inet4Address && mSocket.hasJoinedIpv4()))) {
// Skip sending if the socket has not joined the v4/v6 group (there was no address)
@@ -93,4 +117,37 @@
mSocket.send(new DatagramPacket(outBuffer, 0, len, destination));
}
+
+ /**
+ * Cancel all pending sends.
+ */
+ public void cancelAll() {
+ ensureRunningOnHandlerThread(mHandler);
+ mHandler.removeMessages(MSG_SEND);
+ }
+
+ private class SendHandler extends Handler {
+ SendHandler(@NonNull Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ final ReplyInfo replyInfo = (ReplyInfo) msg.obj;
+ if (DBG) Log.v(mLogTag, "Sending " + replyInfo);
+
+ final int flags = 0x8400; // Response, authoritative (rfc6762 18.4)
+ final MdnsPacket packet = new MdnsPacket(flags,
+ Collections.emptyList() /* questions */,
+ replyInfo.answers,
+ Collections.emptyList() /* authorityRecords */,
+ replyInfo.additionalAnswers);
+
+ try {
+ sendNow(packet, replyInfo.destination);
+ } catch (IOException e) {
+ Log.e(mLogTag, "Error sending MDNS response", e);
+ }
+ }
+ }
}
diff --git a/service-t/src/com/android/server/mdns/MdnsResponse.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsResponse.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
diff --git a/service-t/src/com/android/server/mdns/MdnsResponseDecoder.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
similarity index 69%
rename from service-t/src/com/android/server/mdns/MdnsResponseDecoder.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 50f2069..82da2e4 100644
--- a/service-t/src/com/android/server/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -24,11 +24,9 @@
import com.android.server.connectivity.mdns.util.MdnsLogger;
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. */
@@ -48,12 +46,6 @@
this.serviceType = serviceType;
}
- private static void skipMdnsRecord(MdnsPacketReader reader) throws IOException {
- reader.skip(2 + 4); // skip the class and TTL
- int dataLength = reader.readUInt16();
- reader.skip(dataLength);
- }
-
private static MdnsResponse findResponseWithPointer(
List<MdnsResponse> responses, String[] pointer) {
if (responses != null) {
@@ -120,7 +112,7 @@
int interfaceIndex, @Nullable Network network) {
MdnsPacketReader reader = new MdnsPacketReader(recvbuf, length);
- List<MdnsRecord> records;
+ final MdnsPacket mdnsPacket;
try {
reader.readUInt16(); // transaction ID (not used)
int flags = reader.readUInt16();
@@ -128,111 +120,25 @@
return MdnsResponseErrorCode.ERROR_NOT_RESPONSE_MESSAGE;
}
- int numQuestions = reader.readUInt16();
- int numAnswers = reader.readUInt16();
- int numAuthority = reader.readUInt16();
- int numRecords = reader.readUInt16();
-
- LOGGER.log(String.format(
- "num questions: %d, num answers: %d, num authority: %d, num records: %d",
- numQuestions, numAnswers, numAuthority, numRecords));
-
- if (numAnswers < 1) {
+ mdnsPacket = MdnsPacket.parseRecordsSection(reader, flags);
+ if (mdnsPacket.answers.size() < 1) {
return MdnsResponseErrorCode.ERROR_NO_ANSWERS;
}
-
- records = new LinkedList<>();
-
- for (int i = 0; i < (numAnswers + numAuthority + numRecords); ++i) {
- String[] name;
- try {
- name = reader.readLabels();
- } catch (IOException e) {
- LOGGER.e("Failed to read labels from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_RECORD_NAME;
- }
- int type = reader.readUInt16();
-
- switch (type) {
- case MdnsRecord.TYPE_A: {
- try {
- records.add(new MdnsInetAddressRecord(name, MdnsRecord.TYPE_A, reader));
- } catch (IOException e) {
- LOGGER.e("Failed to read A record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_A_RDATA;
- }
- break;
- }
-
- case MdnsRecord.TYPE_AAAA: {
- try {
- // AAAA should only contain the IPv6 address.
- MdnsInetAddressRecord record =
- new MdnsInetAddressRecord(name, MdnsRecord.TYPE_AAAA, reader);
- if (record.getInet6Address() != null) {
- records.add(record);
- }
- } catch (IOException e) {
- LOGGER.e("Failed to read AAAA record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_AAAA_RDATA;
- }
- break;
- }
-
- case MdnsRecord.TYPE_PTR: {
- try {
- records.add(new MdnsPointerRecord(name, reader));
- } catch (IOException e) {
- LOGGER.e("Failed to read PTR record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_PTR_RDATA;
- }
- break;
- }
-
- case MdnsRecord.TYPE_SRV: {
- if (name.length == 4) {
- try {
- records.add(new MdnsServiceRecord(name, reader));
- } catch (IOException e) {
- LOGGER.e("Failed to read SRV record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_SRV_RDATA;
- }
- } else {
- try {
- skipMdnsRecord(reader);
- } catch (IOException e) {
- LOGGER.e("Failed to skip SVR record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_SKIPPING_SRV_RDATA;
- }
- }
- break;
- }
-
- case MdnsRecord.TYPE_TXT: {
- try {
- records.add(new MdnsTextRecord(name, reader));
- } catch (IOException e) {
- LOGGER.e("Failed to read TXT record from mDNS response.", e);
- return MdnsResponseErrorCode.ERROR_READING_TXT_RDATA;
- }
- break;
- }
-
- default: {
- try {
- skipMdnsRecord(reader);
- } catch (IOException e) {
- LOGGER.e("Failed to skip mDNS record.", e);
- return MdnsResponseErrorCode.ERROR_SKIPPING_UNKNOWN_RECORD;
- }
- }
- }
- }
} catch (EOFException e) {
LOGGER.e("Reached the end of the mDNS response unexpectedly.", e);
return MdnsResponseErrorCode.ERROR_END_OF_FILE;
+ } catch (MdnsPacket.ParseException e) {
+ LOGGER.e(e.getMessage(), e);
+ return e.code;
}
+ final ArrayList<MdnsRecord> records = new ArrayList<>(
+ mdnsPacket.questions.size() + mdnsPacket.answers.size()
+ + mdnsPacket.authorityRecords.size() + mdnsPacket.additionalRecords.size());
+ records.addAll(mdnsPacket.answers);
+ records.addAll(mdnsPacket.authorityRecords);
+ records.addAll(mdnsPacket.additionalRecords);
+
// The response records are structured in a hierarchy, where some records reference
// others, as follows:
//
diff --git a/service-t/src/com/android/server/mdns/MdnsResponseErrorCode.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsResponseErrorCode.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsResponseErrorCode.java
diff --git a/service-t/src/com/android/server/mdns/MdnsSearchOptions.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsSearchOptions.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java
diff --git a/service-t/src/com/android/server/mdns/MdnsServiceBrowserListener.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsServiceBrowserListener.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsServiceBrowserListener.java
diff --git a/service-t/src/com/android/server/mdns/MdnsServiceInfo.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceInfo.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsServiceInfo.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsServiceInfo.java
diff --git a/service-t/src/com/android/server/mdns/MdnsServiceRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsServiceRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
diff --git a/service-t/src/com/android/server/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsServiceTypeClient.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
diff --git a/service-t/src/com/android/server/mdns/MdnsSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsSocket.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java
diff --git a/service-t/src/com/android/server/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsSocketClient.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
diff --git a/service-t/src/com/android/server/mdns/MdnsSocketClientBase.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsSocketClientBase.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
diff --git a/service-t/src/com/android/server/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsSocketProvider.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
diff --git a/service-t/src/com/android/server/mdns/MdnsTextRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MdnsTextRecord.java
rename to service-t/src/com/android/server/connectivity/mdns/MdnsTextRecord.java
diff --git a/service-t/src/com/android/server/mdns/MulticastNetworkInterfaceProvider.java b/service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/MulticastNetworkInterfaceProvider.java
rename to service-t/src/com/android/server/connectivity/mdns/MulticastNetworkInterfaceProvider.java
diff --git a/service-t/src/com/android/server/mdns/MulticastPacketReader.java b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
similarity index 92%
rename from service-t/src/com/android/server/mdns/MulticastPacketReader.java
rename to service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
index 20cc47f..b597f0a 100644
--- a/service-t/src/com/android/server/mdns/MulticastPacketReader.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
@@ -107,5 +107,14 @@
ensureRunningOnHandlerThread(mHandler);
mPacketHandlers.add(handler);
}
+
+ /**
+ * Remove a packet handler added via {@link #addPacketHandler}. If the handler was not set,
+ * this is a no-op.
+ */
+ public void removePacketHandler(@NonNull PacketHandler handler) {
+ ensureRunningOnHandlerThread(mHandler);
+ mPacketHandlers.remove(handler);
+ }
}
diff --git a/service-t/src/com/android/server/mdns/NameConflictException.java b/service-t/src/com/android/server/connectivity/mdns/NameConflictException.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/NameConflictException.java
rename to service-t/src/com/android/server/connectivity/mdns/NameConflictException.java
diff --git a/service-t/src/com/android/server/mdns/NetworkInterfaceWrapper.java b/service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/NetworkInterfaceWrapper.java
rename to service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java
diff --git a/service-t/src/com/android/server/mdns/util/MdnsLogger.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsLogger.java
similarity index 100%
rename from service-t/src/com/android/server/mdns/util/MdnsLogger.java
rename to service-t/src/com/android/server/connectivity/mdns/util/MdnsLogger.java
diff --git a/service-t/src/com/android/server/mdns/MdnsPacket.java b/service-t/src/com/android/server/mdns/MdnsPacket.java
deleted file mode 100644
index eae084a..0000000
--- a/service-t/src/com/android/server/mdns/MdnsPacket.java
+++ /dev/null
@@ -1,43 +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.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-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 5852a30..1606fd0 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -537,6 +537,7 @@
BroadcastOptions.makeBasic())
.setDeliveryGroupPolicy(
ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeferUntilActive(true)
.toBundle();
} catch (UnsupportedApiLevelException e) {
Log.wtf(TAG, "Using unsupported API" + e);
@@ -3269,4 +3270,7 @@
private static native long nativeGetTotalStat(int type);
private static native long nativeGetIfaceStat(String iface, int type);
private static native long nativeGetUidStat(int uid, int type);
+
+ /** Initializes and registers the Perfetto Network Trace data source */
+ public static native void nativeInitNetworkTracing();
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index a7e6a2e..eef4a0e 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -269,6 +269,7 @@
import com.android.networkstack.apishim.common.BroadcastOptionsShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
@@ -407,8 +408,7 @@
private final MockableSystemProperties mSystemProperties;
- @VisibleForTesting
- protected final PermissionMonitor mPermissionMonitor;
+ private final PermissionMonitor mPermissionMonitor;
@VisibleForTesting
final RequestInfoPerUidCounter mNetworkRequestCounter;
@@ -843,7 +843,7 @@
private final LocationPermissionChecker mLocationPermissionChecker;
- private final KeepaliveTracker mKeepaliveTracker;
+ private final AutomaticOnOffKeepaliveTracker mKeepaliveTracker;
private final QosCallbackTracker mQosCallbackTracker;
private final NetworkNotificationManager mNotifier;
private final LingerMonitor mLingerMonitor;
@@ -1565,7 +1565,7 @@
mSettingsObserver = new SettingsObserver(mContext, mHandler);
registerSettingsCallbacks();
- mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
+ mKeepaliveTracker = new AutomaticOnOffKeepaliveTracker(mContext, mHandler);
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager);
mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
@@ -3081,6 +3081,7 @@
optsShim.setDeliveryGroupPolicy(ConstantsShim.DELIVERY_GROUP_POLICY_MOST_RECENT);
optsShim.setDeliveryGroupMatchingKey(ConnectivityManager.CONNECTIVITY_ACTION,
createDeliveryGroupKeyForConnectivityAction(info));
+ optsShim.setDeferUntilActive(true);
} catch (UnsupportedApiLevelException e) {
Log.wtf(TAG, "Using unsupported API" + e);
}
@@ -5544,6 +5545,33 @@
mKeepaliveTracker.handleStartKeepalive(msg);
break;
}
+ case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
+ final Network network = (Network) msg.obj;
+ final int slot = msg.arg1;
+
+ boolean networkFound = false;
+ final ArrayList<NetworkAgentInfo> vpnsRunningOnThisNetwork = new ArrayList<>();
+ for (NetworkAgentInfo n : mNetworkAgentInfos) {
+ if (n.network.equals(network)) networkFound = true;
+ if (n.isVPN() && n.everConnected() && hasUnderlyingNetwork(n, network)) {
+ vpnsRunningOnThisNetwork.add(n);
+ }
+ }
+
+ // If the network no longer exists, then the keepalive should have been
+ // cleaned up already. There is no point trying to resume keepalives.
+ if (!networkFound) return;
+
+ if (!vpnsRunningOnThisNetwork.isEmpty()) {
+ mKeepaliveTracker.handleMonitorAutomaticKeepalive(network, slot,
+ // TODO: check all the VPNs running on top of this network
+ vpnsRunningOnThisNetwork.get(0).network.netId);
+ } else {
+ // If no VPN, then make sure the keepalive is running.
+ mKeepaliveTracker.handleMaybeResumeKeepalive(network, slot);
+ }
+ break;
+ }
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE: {
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
@@ -6217,9 +6245,7 @@
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
- if (!mProfileNetworkPreferences.isEmpty()) {
- updateProfileAllowedNetworks();
- }
+ updateProfileAllowedNetworks();
}
private void onUserRemoved(@NonNull final UserHandle user) {
@@ -9788,20 +9814,23 @@
enforceKeepalivePermission();
mKeepaliveTracker.startNattKeepalive(
getNetworkAgentInfoForNetwork(network), null /* fd */,
- intervalSeconds, cb,
- srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
+ intervalSeconds, cb, srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT,
+ // Keep behavior of the deprecated method as it is. Set automaticOnOffKeepalives to
+ // false because there is no way and no plan to configure automaticOnOffKeepalives
+ // in this deprecated method.
+ false /* automaticOnOffKeepalives */);
}
@Override
public void startNattKeepaliveWithFd(Network network, ParcelFileDescriptor pfd, int resourceId,
int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
- String dstAddr) {
+ String dstAddr, boolean automaticOnOffKeepalives) {
try {
final FileDescriptor fd = pfd.getFileDescriptor();
mKeepaliveTracker.startNattKeepalive(
getNetworkAgentInfoForNetwork(network), fd, resourceId,
intervalSeconds, cb,
- srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
+ srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT, automaticOnOffKeepalives);
} finally {
// FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
// startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
new file mode 100644
index 0000000..27be545
--- /dev/null
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2023 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.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
+import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.SUCCESS;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
+import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
+import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
+import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
+import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.INetd;
+import android.net.ISocketKeepaliveCallback;
+import android.net.MarkMaskParcel;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.SocketKeepalive;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.HexDump;
+import com.android.net.module.util.SocketUtils;
+import com.android.net.module.util.netlink.InetDiagMessage;
+import com.android.net.module.util.netlink.NetlinkUtils;
+import com.android.net.module.util.netlink.StructNlAttr;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.SocketException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages automatic on/off socket keepalive requests.
+ *
+ * Provides methods to stop and start automatic keepalive requests, and keeps track of keepalives
+ * across all networks. For non-automatic on/off keepalive request, this class just forwards the
+ * requests to KeepaliveTracker. This class is tightly coupled to ConnectivityService. It is not
+ * thread-safe and its handle* methods must be called only from the ConnectivityService handler
+ * thread.
+ */
+public class AutomaticOnOffKeepaliveTracker {
+ private static final String TAG = "AutomaticOnOffKeepaliveTracker";
+ private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
+ private static final String ACTION_TCP_POLLING_ALARM =
+ "com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM";
+ private static final String EXTRA_NETWORK = "network_id";
+ private static final String EXTRA_SLOT = "slot";
+ private static final long DEFAULT_TCP_POLLING_INTERVAL_MS = 120_000L;
+ private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
+ "automatic_on_off_keepalive_version";
+ /**
+ * States for {@code #AutomaticOnOffKeepalive}.
+ *
+ * A new AutomaticOnOffKeepalive starts with STATE_ENABLED. The system will monitor
+ * the TCP sockets on VPN networks running on top of the specified network, and turn off
+ * keepalive if there is no TCP socket any of the VPN networks. Conversely, it will turn
+ * keepalive back on if any TCP socket is open on any of the VPN networks.
+ *
+ * When there is no TCP socket on any of the VPN networks, the state becomes STATE_SUSPENDED.
+ * The {@link KeepaliveTracker.KeepaliveInfo} object is kept to remember the parameters so it
+ * is possible to resume keepalive later with the same parameters.
+ *
+ * When the system detects some TCP socket is open on one of the VPNs while in STATE_SUSPENDED,
+ * this AutomaticOnOffKeepalive goes to STATE_ENABLED again.
+ *
+ * When finishing keepalive, this object is deleted.
+ */
+ private static final int STATE_ENABLED = 0;
+ private static final int STATE_SUSPENDED = 1;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_ENABLED,
+ STATE_SUSPENDED
+ })
+ private @interface AutomaticOnOffState {}
+
+ @NonNull
+ private final Handler mConnectivityServiceHandler;
+ @NonNull
+ private final KeepaliveTracker mKeepaliveTracker;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final AlarmManager mAlarmManager;
+
+ /**
+ * The {@code inetDiagReqV2} messages for different IP family.
+ *
+ * Key: Ip family type.
+ * Value: Bytes array represent the {@code inetDiagReqV2}.
+ *
+ * This should only be accessed in the connectivity service handler thread.
+ */
+ private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
+ private final Dependencies mDependencies;
+ private final INetd mNetd;
+ /**
+ * Keeps track of automatic on/off keepalive requests.
+ * This should be only updated in ConnectivityService handler thread.
+ */
+ private final ArrayList<AutomaticOnOffKeepalive> mAutomaticOnOffKeepalives = new ArrayList<>();
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_TCP_POLLING_ALARM.equals(intent.getAction())) {
+ Log.d(TAG, "Received TCP polling intent");
+ final Network network = intent.getParcelableExtra(EXTRA_NETWORK);
+ final int slot = intent.getIntExtra(EXTRA_SLOT, -1);
+ mConnectivityServiceHandler.obtainMessage(
+ NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE,
+ slot, 0 , network).sendToTarget();
+ }
+ }
+ };
+
+ private static class AutomaticOnOffKeepalive {
+ @NonNull
+ private final KeepaliveTracker.KeepaliveInfo mKi;
+ @NonNull
+ private final FileDescriptor mFd;
+ @NonNull
+ private final PendingIntent mTcpPollingAlarm;
+ private final int mSlot;
+ @AutomaticOnOffState
+ private int mAutomaticOnOffState = STATE_ENABLED;
+
+ AutomaticOnOffKeepalive(@NonNull KeepaliveTracker.KeepaliveInfo ki,
+ @NonNull Context context) throws InvalidSocketException {
+ this.mKi = Objects.requireNonNull(ki);
+ // A null fd is acceptable in KeepaliveInfo for backward compatibility of
+ // PacketKeepalive API, but it should not happen here because legacy API cannot setup
+ // automatic keepalive.
+ Objects.requireNonNull(ki.mFd);
+
+ // Get the slot from keepalive because the slot information may be missing when the
+ // keepalive is stopped.
+ this.mSlot = ki.getSlot();
+ try {
+ this.mFd = Os.dup(ki.mFd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot dup fd: ", e);
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ }
+ mTcpPollingAlarm = createTcpPollingAlarmIntent(
+ context, ki.getNai().network(), ki.getSlot());
+ }
+
+ public boolean match(Network network, int slot) {
+ return this.mKi.getNai().network().equals(network) && this.mSlot == slot;
+ }
+
+ private static PendingIntent createTcpPollingAlarmIntent(@NonNull Context context,
+ @NonNull Network network, int slot) {
+ final Intent intent = new Intent(ACTION_TCP_POLLING_ALARM);
+ intent.putExtra(EXTRA_NETWORK, network);
+ intent.putExtra(EXTRA_SLOT, slot);
+ return PendingIntent.getBroadcast(
+ context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+ }
+
+ public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler) {
+ this(context, handler, new Dependencies(context));
+ }
+
+ @VisibleForTesting
+ public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler,
+ @NonNull Dependencies dependencies) {
+ mContext = Objects.requireNonNull(context);
+ mDependencies = Objects.requireNonNull(dependencies);
+ mConnectivityServiceHandler = Objects.requireNonNull(handler);
+ mNetd = mDependencies.getNetd();
+ mKeepaliveTracker = mDependencies.newKeepaliveTracker(
+ mContext, mConnectivityServiceHandler);
+
+ if (SdkLevel.isAtLeastU()) {
+ mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TCP_POLLING_ALARM),
+ null, handler);
+ }
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ }
+
+ private void startTcpPollingAlarm(@NonNull PendingIntent alarm) {
+ final long triggerAtMillis =
+ SystemClock.elapsedRealtime() + DEFAULT_TCP_POLLING_INTERVAL_MS;
+ // Setup a non-wake up alarm.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, alarm);
+ }
+
+ /**
+ * Determine if any state transition is needed for the specific automatic keepalive.
+ */
+ public void handleMonitorAutomaticKeepalive(@NonNull Network network, int slot, int vpnNetId) {
+ final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(network, slot);
+ // This may happen if the keepalive is removed by the app, and the alarm is fired at the
+ // same time.
+ if (autoKi == null) return;
+
+ handleMonitorTcpConnections(autoKi, vpnNetId);
+ }
+
+ /**
+ * Determine if disable or re-enable keepalive is needed or not based on TCP sockets status.
+ */
+ private void handleMonitorTcpConnections(@NonNull AutomaticOnOffKeepalive ki, int vpnNetId) {
+ if (!isAnyTcpSocketConnected(vpnNetId)) {
+ // No TCP socket exists. Stop keepalive if ENABLED, and remain SUSPENDED if currently
+ // SUSPENDED.
+ if (ki.mAutomaticOnOffState == STATE_ENABLED) {
+ ki.mAutomaticOnOffState = STATE_SUSPENDED;
+ handleSuspendKeepalive(ki.mKi.mNai, ki.mSlot, SUCCESS);
+ }
+ } else {
+ handleMaybeResumeKeepalive(ki);
+ }
+ // TODO: listen to socket status instead of periodically check.
+ startTcpPollingAlarm(ki.mTcpPollingAlarm);
+ }
+
+ /**
+ * Resume keepalive for this slot on this network, if it wasn't already resumed.
+ */
+ public void handleMaybeResumeKeepalive(@NonNull final Network network, final int slot) {
+ final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(network, slot);
+ // This may happen if the keepalive is removed by the app, and the alarm is fired at
+ // the same time.
+ if (autoKi == null) return;
+ handleMaybeResumeKeepalive(autoKi);
+ }
+
+ private void handleMaybeResumeKeepalive(@NonNull AutomaticOnOffKeepalive autoKi) {
+ if (autoKi.mAutomaticOnOffState == STATE_ENABLED) return;
+ KeepaliveTracker.KeepaliveInfo newKi;
+ try {
+ // Get fd from AutomaticOnOffKeepalive since the fd in the original
+ // KeepaliveInfo should be closed.
+ newKi = autoKi.mKi.withFd(autoKi.mFd);
+ } catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
+ Log.e(TAG, "Fail to construct keepalive", e);
+ mKeepaliveTracker.notifyErrorCallback(autoKi.mKi.mCallback, ERROR_INVALID_SOCKET);
+ return;
+ }
+ autoKi.mAutomaticOnOffState = STATE_ENABLED;
+ handleResumeKeepalive(mConnectivityServiceHandler.obtainMessage(
+ NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
+ autoKi.mAutomaticOnOffState, 0, newKi));
+ }
+
+ private int findAutomaticOnOffKeepaliveIndex(@NonNull Network network, int slot) {
+ ensureRunningOnHandlerThread();
+
+ int index = 0;
+ for (AutomaticOnOffKeepalive ki : mAutomaticOnOffKeepalives) {
+ if (ki.match(network, slot)) {
+ return index;
+ }
+ index++;
+ }
+ return -1;
+ }
+
+ @Nullable
+ private AutomaticOnOffKeepalive findAutomaticOnOffKeepalive(@NonNull Network network,
+ int slot) {
+ ensureRunningOnHandlerThread();
+
+ final int index = findAutomaticOnOffKeepaliveIndex(network, slot);
+ return (index >= 0) ? mAutomaticOnOffKeepalives.get(index) : null;
+ }
+
+ /**
+ * Handle keepalive events from lower layer.
+ *
+ * Forward to KeepaliveTracker.
+ */
+ public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) {
+ mKeepaliveTracker.handleEventSocketKeepalive(nai, slot, reason);
+ }
+
+ /**
+ * Handle stop all keepalives on the specific network.
+ */
+ public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
+ mKeepaliveTracker.handleStopAllKeepalives(nai, reason);
+ final List<AutomaticOnOffKeepalive> matches =
+ CollectionUtils.filter(mAutomaticOnOffKeepalives, it -> it.mKi.getNai() == nai);
+ for (final AutomaticOnOffKeepalive ki : matches) {
+ cleanupAutoOnOffKeepalive(ki);
+ }
+ }
+
+ /**
+ * Handle start keepalive contained within a message.
+ *
+ * The message is expected to contain a KeepaliveTracker.KeepaliveInfo.
+ */
+ public void handleStartKeepalive(Message message) {
+ mKeepaliveTracker.handleStartKeepalive(message);
+
+ // Add automatic on/off request into list to track its life cycle.
+ final boolean automaticOnOff = message.arg1 != 0
+ && mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION);
+ if (automaticOnOff) {
+ final KeepaliveTracker.KeepaliveInfo ki = (KeepaliveTracker.KeepaliveInfo) message.obj;
+ AutomaticOnOffKeepalive autoKi;
+ try {
+ // CAREFUL : mKeepaliveTracker.handleStartKeepalive will assign |ki.mSlot| after
+ // pulling |ki| from the message. The constructor below will read this member
+ // (through ki.getSlot()) and therefore actively relies on handleStartKeepalive
+ // having assigned this member before this is called.
+ // TODO : clean this up by assigning the slot at the start of this method instead
+ // and ideally removing the mSlot member from KeepaliveInfo.
+ autoKi = new AutomaticOnOffKeepalive(ki, mContext);
+ } catch (SocketKeepalive.InvalidSocketException | IllegalArgumentException e) {
+ Log.e(TAG, "Fail to construct keepalive", e);
+ mKeepaliveTracker.notifyErrorCallback(ki.mCallback, ERROR_INVALID_SOCKET);
+ return;
+ }
+ mAutomaticOnOffKeepalives.add(autoKi);
+ startTcpPollingAlarm(autoKi.mTcpPollingAlarm);
+ }
+ }
+
+ private void handleResumeKeepalive(Message message) {
+ mKeepaliveTracker.handleStartKeepalive(message);
+ }
+
+ private void handleSuspendKeepalive(NetworkAgentInfo nai, int slot, int reason) {
+ mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
+ }
+
+ /**
+ * Handle stop keepalives on the specific network with given slot.
+ */
+ public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
+ final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(nai.network, slot);
+
+ // Let the original keepalive do the stop first, and then clean up the keepalive if it's an
+ // automatic keepalive.
+ if (autoKi == null || autoKi.mAutomaticOnOffState == STATE_ENABLED) {
+ mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
+ }
+
+ // Not an AutomaticOnOffKeepalive.
+ if (autoKi == null) return;
+
+ cleanupAutoOnOffKeepalive(autoKi);
+ }
+
+ private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
+ ensureRunningOnHandlerThread();
+ mAlarmManager.cancel(autoKi.mTcpPollingAlarm);
+ // Close the duplicated fd that maintains the lifecycle of socket.
+ FileUtils.closeQuietly(autoKi.mFd);
+ mAutomaticOnOffKeepalives.remove(autoKi);
+ }
+
+ /**
+ * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
+ * {@link android.net.SocketKeepalive}.
+ *
+ * Forward to KeepaliveTracker.
+ **/
+ public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ @Nullable FileDescriptor fd,
+ int intervalSeconds,
+ @NonNull ISocketKeepaliveCallback cb,
+ @NonNull String srcAddrString,
+ int srcPort,
+ @NonNull String dstAddrString,
+ int dstPort, boolean automaticOnOffKeepalives) {
+ final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
+ intervalSeconds, cb, srcAddrString, srcPort, dstAddrString, dstPort);
+ if (null != ki) {
+ mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
+ // TODO : move ConnectivityService#encodeBool to a static lib.
+ automaticOnOffKeepalives ? 1 : 0, 0, ki).sendToTarget();
+ }
+ }
+
+ /**
+ * Called when requesting that keepalives be started on a IPsec NAT-T socket. See
+ * {@link android.net.SocketKeepalive}.
+ *
+ * Forward to KeepaliveTracker.
+ **/
+ public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ @Nullable FileDescriptor fd,
+ int resourceId,
+ int intervalSeconds,
+ @NonNull ISocketKeepaliveCallback cb,
+ @NonNull String srcAddrString,
+ @NonNull String dstAddrString,
+ int dstPort,
+ boolean automaticOnOffKeepalives) {
+ final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
+ resourceId, intervalSeconds, cb, srcAddrString, dstAddrString, dstPort);
+ if (null != ki) {
+ mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
+ // TODO : move ConnectivityService#encodeBool to a static lib.
+ automaticOnOffKeepalives ? 1 : 0, 0, ki).sendToTarget();
+ }
+ }
+
+ /**
+ * Called by ConnectivityService to start TCP keepalive on a file descriptor.
+ *
+ * In order to offload keepalive for application correctly, sequence number, ack number and
+ * other fields are needed to form the keepalive packet. Thus, this function synchronously
+ * puts the socket into repair mode to get the necessary information. After the socket has been
+ * put into repair mode, the application cannot access the socket until reverted to normal.
+ * See {@link android.net.SocketKeepalive}.
+ *
+ * Forward to KeepaliveTracker.
+ **/
+ public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
+ @NonNull FileDescriptor fd,
+ int intervalSeconds,
+ @NonNull ISocketKeepaliveCallback cb) {
+ final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeTcpKeepaliveInfo(nai, fd,
+ intervalSeconds, cb);
+ if (null != ki) {
+ mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki)
+ .sendToTarget();
+ }
+ }
+
+ /**
+ * Dump AutomaticOnOffKeepaliveTracker state.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ // TODO: Dump the necessary information for automatic on/off keepalive.
+ mKeepaliveTracker.dump(pw);
+ }
+
+ /**
+ * Check all keepalives on the network are still valid.
+ *
+ * Forward to KeepaliveTracker.
+ */
+ public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
+ mKeepaliveTracker.handleCheckKeepalivesStillValid(nai);
+ }
+
+ @VisibleForTesting
+ boolean isAnyTcpSocketConnected(int netId) {
+ FileDescriptor fd = null;
+
+ try {
+ fd = mDependencies.createConnectedNetlinkSocket();
+
+ // Get network mask
+ final MarkMaskParcel parcel = mNetd.getFwmarkForNetwork(netId);
+ final int networkMark = (parcel != null) ? parcel.mark : NetlinkUtils.UNKNOWN_MARK;
+ final int networkMask = (parcel != null) ? parcel.mask : NetlinkUtils.NULL_MASK;
+
+ // Send request for each IP family
+ for (final int family : ADDRESS_FAMILIES) {
+ if (isAnyTcpSocketConnectedForFamily(fd, family, networkMark, networkMask)) {
+ return true;
+ }
+ }
+ } catch (ErrnoException | SocketException | InterruptedIOException | RemoteException e) {
+ Log.e(TAG, "Fail to get socket info via netlink.", e);
+ } finally {
+ SocketUtils.closeSocketQuietly(fd);
+ }
+
+ return false;
+ }
+
+ private boolean isAnyTcpSocketConnectedForFamily(FileDescriptor fd, int family, int networkMark,
+ int networkMask) throws ErrnoException, InterruptedIOException {
+ ensureRunningOnHandlerThread();
+ // Build SocketDiag messages and cache it.
+ if (mSockDiagMsg.get(family) == null) {
+ mSockDiagMsg.put(family, InetDiagMessage.buildInetDiagReqForAliveTcpSockets(family));
+ }
+ mDependencies.sendRequest(fd, mSockDiagMsg.get(family));
+
+ // Iteration limitation as a protection to avoid possible infinite loops.
+ // DEFAULT_RECV_BUFSIZE could read more than 20 sockets per time. Max iteration
+ // should be enough to go through reasonable TCP sockets in the device.
+ final int maxIteration = 100;
+ int parsingIteration = 0;
+ while (parsingIteration < maxIteration) {
+ final ByteBuffer bytes = mDependencies.recvSockDiagResponse(fd);
+
+ try {
+ while (NetlinkUtils.enoughBytesRemainForValidNlMsg(bytes)) {
+ final int startPos = bytes.position();
+
+ final int nlmsgLen = bytes.getInt();
+ final int nlmsgType = bytes.getShort();
+ if (isEndOfMessageOrError(nlmsgType)) return false;
+ // TODO: Parse InetDiagMessage to get uid and dst address information to filter
+ // socket via NetlinkMessage.parse.
+
+ // Skip the header to move to data part.
+ bytes.position(startPos + SOCKDIAG_MSG_HEADER_SIZE);
+
+ if (isTargetTcpSocket(bytes, nlmsgLen, networkMark, networkMask)) {
+ return true;
+ }
+ }
+ } catch (BufferUnderflowException e) {
+ // The exception happens in random place in either header position or any data
+ // position. Partial bytes from the middle of the byte buffer may not be enough to
+ // clarify, so print out the content before the error to possibly prevent printing
+ // the whole 8K buffer.
+ final int exceptionPos = bytes.position();
+ final String hex = HexDump.dumpHexString(bytes.array(), 0, exceptionPos);
+ Log.e(TAG, "Unexpected socket info parsing: " + hex, e);
+ }
+
+ parsingIteration++;
+ }
+ return false;
+ }
+
+ private boolean isEndOfMessageOrError(int nlmsgType) {
+ return nlmsgType == NLMSG_DONE || nlmsgType != SOCK_DIAG_BY_FAMILY;
+ }
+
+ private boolean isTargetTcpSocket(@NonNull ByteBuffer bytes, int nlmsgLen, int networkMark,
+ int networkMask) {
+ final int mark = readSocketDataAndReturnMark(bytes, nlmsgLen);
+ return (mark & networkMask) == networkMark;
+ }
+
+ private int readSocketDataAndReturnMark(@NonNull ByteBuffer bytes, int nlmsgLen) {
+ final int nextMsgOffset = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE;
+ int mark = NetlinkUtils.INIT_MARK_VALUE;
+ // Get socket mark
+ // TODO: Add a parsing method in NetlinkMessage.parse to support this to skip the remaining
+ // data.
+ while (bytes.position() < nextMsgOffset) {
+ final StructNlAttr nlattr = StructNlAttr.parse(bytes);
+ if (nlattr != null && nlattr.nla_type == NetlinkUtils.INET_DIAG_MARK) {
+ mark = nlattr.getValueAsInteger();
+ }
+ }
+ return mark;
+ }
+
+ private void ensureRunningOnHandlerThread() {
+ if (mConnectivityServiceHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on handler thread: " + Thread.currentThread().getName());
+ }
+ }
+
+ /**
+ * Dependencies class for testing.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ private final Context mContext;
+
+ public Dependencies(final Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Create a netlink socket connected to the kernel.
+ *
+ * @return fd the fileDescriptor of the socket.
+ */
+ public FileDescriptor createConnectedNetlinkSocket()
+ throws ErrnoException, SocketException {
+ final FileDescriptor fd = NetlinkUtils.createNetLinkInetDiagSocket();
+ NetlinkUtils.connectSocketToNetlink(fd);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO,
+ StructTimeval.fromMillis(IO_TIMEOUT_MS));
+ return fd;
+ }
+
+ /**
+ * Send composed message request to kernel.
+ *
+ * The given FileDescriptor is expected to be created by
+ * {@link #createConnectedNetlinkSocket} or equivalent way.
+ *
+ * @param fd a netlink socket {@code FileDescriptor} connected to the kernel.
+ * @param msg the byte array representing the request message to write to kernel.
+ */
+ public void sendRequest(@NonNull final FileDescriptor fd,
+ @NonNull final byte[] msg)
+ throws ErrnoException, InterruptedIOException {
+ Os.write(fd, msg, 0 /* byteOffset */, msg.length);
+ }
+
+ /**
+ * Get an INetd connector.
+ */
+ public INetd getNetd() {
+ return INetd.Stub.asInterface(
+ (IBinder) mContext.getSystemService(Context.NETD_SERVICE));
+ }
+
+ /**
+ * Receive the response message from kernel via given {@code FileDescriptor}.
+ * The usage should follow the {@code #sendRequest} call with the same
+ * FileDescriptor.
+ *
+ * The overall response may be large but the individual messages should not be
+ * excessively large(8-16kB) because trying to get the kernel to return
+ * everything in one big buffer is inefficient as it forces the kernel to allocate
+ * large chunks of linearly physically contiguous memory. The usage should iterate the
+ * call of this method until the end of the overall message.
+ *
+ * The default receiving buffer size should be small enough that it is always
+ * processed within the {@link NetlinkUtils#IO_TIMEOUT_MS} timeout.
+ */
+ public ByteBuffer recvSockDiagResponse(@NonNull final FileDescriptor fd)
+ throws ErrnoException, InterruptedIOException {
+ return NetlinkUtils.recvMessage(
+ fd, NetlinkUtils.DEFAULT_RECV_BUFSIZE, NetlinkUtils.IO_TIMEOUT_MS);
+ }
+
+ /**
+ * Construct a new KeepaliveTracker.
+ */
+ public KeepaliveTracker newKeepaliveTracker(@NonNull Context context,
+ @NonNull Handler connectivityserviceHander) {
+ return new KeepaliveTracker(mContext, connectivityserviceHander);
+ }
+
+ /**
+ * Find out if a feature is enabled from DeviceConfig.
+ *
+ * @param name The name of the property to look up.
+ * @return whether the feature is enabled
+ */
+ public boolean isFeatureEnabled(@NonNull final String name) {
+ return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_CONNECTIVITY, name);
+ }
+ }
+}
diff --git a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
index b06c8aa..4325763 100644
--- a/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
+++ b/service/src/com/android/server/connectivity/CarrierPrivilegeAuthenticator.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.HandlerExecutor;
import com.android.networkstack.apishim.TelephonyManagerShimImpl;
import com.android.networkstack.apishim.common.TelephonyManagerShim;
import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim;
@@ -46,7 +47,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
/**
* Tracks the uid of the carrier privileged app that provides the carrier config.
@@ -105,27 +105,6 @@
}
/**
- * An adapter {@link Executor} that posts all executed tasks onto the given
- * {@link Handler}.
- *
- * TODO : migrate to the version in frameworks/libs/net when it's ready
- *
- * @hide
- */
- public class HandlerExecutor implements Executor {
- private final Handler mHandler;
- public HandlerExecutor(@NonNull Handler handler) {
- mHandler = handler;
- }
- @Override
- public void execute(Runnable command) {
- if (!mHandler.post(command)) {
- throw new RejectedExecutionException(mHandler + " is shutting down");
- }
- }
- }
-
- /**
* Broadcast receiver for ACTION_MULTI_SIM_CONFIG_CHANGED
*
* <p>The broadcast receiver is registered with mHandler
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index 2303894..87ae0c9 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -23,7 +23,7 @@
import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
-import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
+import static com.android.net.module.util.BitUtils.describeDifferences;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -338,31 +338,14 @@
/**
* Returns a short but human-readable string of updates from an older score.
- * @param old the old capabilities to diff from
+ * @param old the old score to diff from
* @return a string fit for logging differences, or null if no differences.
- * this method cannot return the empty string.
+ * this method cannot return the empty string. See BitUtils#describeDifferences.
*/
@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();
+ return describeDifferences(oldPolicies, mPolicies, FullScore::policyNameOf);
}
// Example output :
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 9c36760..03f8f3e 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -18,7 +18,6 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NattSocketKeepalive.NATT_PORT;
-import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
import static android.net.SocketKeepalive.BINDER_DIED;
import static android.net.SocketKeepalive.DATA_RECEIVED;
import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
@@ -33,27 +32,15 @@
import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
import static android.net.SocketKeepalive.NO_KEEPALIVE;
import static android.net.SocketKeepalive.SUCCESS;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_SNDTIMEO;
-
-import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
-import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE;
-import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
-import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.net.ConnectivityResources;
-import android.net.INetd;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
import android.net.InvalidPacketException;
import android.net.KeepalivePacketData;
-import android.net.MarkMaskParcel;
import android.net.NattKeepalivePacketData;
import android.net.NetworkAgent;
import android.net.SocketKeepalive.InvalidSocketException;
@@ -67,29 +54,18 @@
import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.StructTimeval;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import com.android.connectivity.resources.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.IpUtils;
-import com.android.net.module.util.SocketUtils;
-import com.android.net.module.util.netlink.InetDiagMessage;
-import com.android.net.module.util.netlink.NetlinkUtils;
-import com.android.net.module.util.netlink.StructNlAttr;
import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -107,12 +83,10 @@
private static final boolean DBG = false;
public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
- private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
/** Keeps track of keepalive requests. */
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
new HashMap<> ();
- private final Handler mConnectivityServiceHandler;
@NonNull
private final TcpKeepaliveController mTcpController;
@NonNull
@@ -131,35 +105,17 @@
// Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if
// the number of remaining keepalive slots is less than or equal to the threshold.
private final int mAllowedUnprivilegedSlotsForUid;
- /**
- * The {@code inetDiagReqV2} messages for different IP family.
- *
- * Key: Ip family type.
- * Value: Bytes array represent the {@code inetDiagReqV2}.
- *
- * This should only be accessed in the connectivity service handler thread.
- */
- private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
- private final Dependencies mDependencies;
- private final INetd mNetd;
public KeepaliveTracker(Context context, Handler handler) {
- this(context, handler, new Dependencies(context));
- }
-
- @VisibleForTesting
- public KeepaliveTracker(Context context, Handler handler, Dependencies dependencies) {
- mConnectivityServiceHandler = handler;
mTcpController = new TcpKeepaliveController(handler);
mContext = context;
- mDependencies = dependencies;
- mSupportedKeepalives = mDependencies.getSupportedKeepalives();
- mNetd = mDependencies.getNetd();
- final Resources res = mDependencies.newConnectivityResources();
- mReservedPrivilegedSlots = res.getInteger(
+ mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
+
+ final ConnectivityResources res = new ConnectivityResources(mContext);
+ mReservedPrivilegedSlots = res.get().getInteger(
R.integer.config_reservedPrivilegedKeepaliveSlots);
- mAllowedUnprivilegedSlotsForUid = res.getInteger(
+ mAllowedUnprivilegedSlotsForUid = res.get().getInteger(
R.integer.config_allowedUnprivilegedKeepalivePerUid);
}
@@ -171,13 +127,13 @@
*/
class KeepaliveInfo implements IBinder.DeathRecipient {
// Bookkeeping data.
- private final ISocketKeepaliveCallback mCallback;
+ public final ISocketKeepaliveCallback mCallback;
private final int mUid;
private final int mPid;
private final boolean mPrivileged;
- private final NetworkAgentInfo mNai;
+ public final NetworkAgentInfo mNai;
private final int mType;
- private final FileDescriptor mFd;
+ public final FileDescriptor mFd;
public static final int TYPE_NATT = 1;
public static final int TYPE_TCP = 2;
@@ -285,6 +241,10 @@
}
}
+ public int getSlot() {
+ return mSlot;
+ }
+
private int checkNetworkConnected() {
if (!mNai.networkInfo.isConnectedOrConnecting()) {
return ERROR_INVALID_NETWORK;
@@ -457,6 +417,13 @@
void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
}
+
+ /**
+ * Construct a new KeepaliveInfo from existing KeepaliveInfo with a new fd.
+ */
+ public KeepaliveInfo withFd(@NonNull FileDescriptor fd) throws InvalidSocketException {
+ return new KeepaliveInfo(mCallback, mNai, mPacket, mInterval, mType, fd);
+ }
}
void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
@@ -486,6 +453,9 @@
return slot;
}
+ /**
+ * Handle start keepalives with the message.
+ */
public void handleStartKeepalive(Message message) {
KeepaliveInfo ki = (KeepaliveInfo) message.obj;
NetworkAgentInfo nai = ki.getNai();
@@ -646,7 +616,8 @@
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
* {@link android.net.SocketKeepalive}.
**/
- public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ @Nullable
+ public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
@Nullable FileDescriptor fd,
int intervalSeconds,
@NonNull ISocketKeepaliveCallback cb,
@@ -656,7 +627,7 @@
int dstPort) {
if (nai == null) {
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
- return;
+ return null;
}
InetAddress srcAddress, dstAddress;
@@ -665,7 +636,7 @@
dstAddress = InetAddresses.parseNumericAddress(dstAddrString);
} catch (IllegalArgumentException e) {
notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
- return;
+ return null;
}
KeepalivePacketData packet;
@@ -674,7 +645,7 @@
srcAddress, srcPort, dstAddress, NATT_PORT);
} catch (InvalidPacketException e) {
notifyErrorCallback(cb, e.getError());
- return;
+ return null;
}
KeepaliveInfo ki = null;
try {
@@ -683,15 +654,14 @@
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive", e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
- return;
+ return null;
}
- Log.d(TAG, "Created keepalive: " + ki.toString());
- mConnectivityServiceHandler.obtainMessage(
- NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
+ Log.d(TAG, "Created keepalive: " + ki);
+ return ki;
}
/**
- * Called by ConnectivityService to start TCP keepalive on a file descriptor.
+ * Make a KeepaliveInfo for a TCP socket.
*
* In order to offload keepalive for application correctly, sequence number, ack number and
* other fields are needed to form the keepalive packet. Thus, this function synchronously
@@ -700,13 +670,14 @@
*
* See {@link android.net.SocketKeepalive}.
**/
- public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
+ @Nullable
+ public KeepaliveInfo makeTcpKeepaliveInfo(@Nullable NetworkAgentInfo nai,
@NonNull FileDescriptor fd,
int intervalSeconds,
@NonNull ISocketKeepaliveCallback cb) {
if (nai == null) {
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
- return;
+ return null;
}
final TcpKeepalivePacketData packet;
@@ -714,10 +685,10 @@
packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
} catch (InvalidSocketException e) {
notifyErrorCallback(cb, e.error);
- return;
+ return null;
} catch (InvalidPacketException e) {
notifyErrorCallback(cb, e.getError());
- return;
+ return null;
}
KeepaliveInfo ki = null;
try {
@@ -726,20 +697,22 @@
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive e=" + e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
- return;
+ return null;
}
Log.d(TAG, "Created keepalive: " + ki.toString());
- mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
+ return ki;
}
- /**
- * Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is
- * identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the
- * resource index bound to the {@link UdpEncapsulationSocket} when creating by
- * {@link com.android.server.IpSecService} to verify whether the given
- * {@link UdpEncapsulationSocket} is legitimate.
- **/
- public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
+ /**
+ * Make a KeepaliveInfo for an IPSec NAT-T socket.
+ *
+ * This function is identical to {@link #makeNattKeepaliveInfo}, but also takes a
+ * {@code resourceId}, which is the resource index bound to the {@link UdpEncapsulationSocket}
+ * when creating by {@link com.android.server.IpSecService} to verify whether the given
+ * {@link UdpEncapsulationSocket} is legitimate.
+ **/
+ @Nullable
+ public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
@Nullable FileDescriptor fd,
int resourceId,
int intervalSeconds,
@@ -750,6 +723,7 @@
// Ensure that the socket is created by IpSecService.
if (!isNattKeepaliveSocketValid(fd, resourceId)) {
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
+ return null;
}
// Get src port to adopt old API.
@@ -759,10 +733,11 @@
srcPort = ((InetSocketAddress) srcSockAddr).getPort();
} catch (ErrnoException e) {
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
+ return null;
}
// Forward request to old API.
- startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
+ return makeNattKeepaliveInfo(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
dstAddrString, dstPort);
}
@@ -801,196 +776,4 @@
}
pw.decreaseIndent();
}
-
- /**
- * Dependencies class for testing.
- */
- @VisibleForTesting
- public static class Dependencies {
- private final Context mContext;
-
- public Dependencies(final Context context) {
- mContext = context;
- }
-
- /**
- * Create a netlink socket connected to the kernel.
- *
- * @return fd the fileDescriptor of the socket.
- */
- public FileDescriptor createConnectedNetlinkSocket()
- throws ErrnoException, SocketException {
- final FileDescriptor fd = NetlinkUtils.createNetLinkInetDiagSocket();
- NetlinkUtils.connectSocketToNetlink(fd);
- Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO,
- StructTimeval.fromMillis(IO_TIMEOUT_MS));
- return fd;
- }
-
- /**
- * Send composed message request to kernel.
- *
- * The given FileDescriptor is expected to be created by
- * {@link #createConnectedNetlinkSocket} or equivalent way.
- *
- * @param fd a netlink socket {@code FileDescriptor} connected to the kernel.
- * @param msg the byte array representing the request message to write to kernel.
- */
- public void sendRequest(@NonNull final FileDescriptor fd,
- @NonNull final byte[] msg)
- throws ErrnoException, InterruptedIOException {
- Os.write(fd, msg, 0 /* byteOffset */, msg.length);
- }
-
- /**
- * Get an INetd connector.
- */
- public INetd getNetd() {
- return INetd.Stub.asInterface(
- (IBinder) mContext.getSystemService(Context.NETD_SERVICE));
- }
-
- /**
- * Receive the response message from kernel via given {@code FileDescriptor}.
- * The usage should follow the {@code #sendRequest} call with the same
- * FileDescriptor.
- *
- * The overall response may be large but the individual messages should not be
- * excessively large(8-16kB) because trying to get the kernel to return
- * everything in one big buffer is inefficient as it forces the kernel to allocate
- * large chunks of linearly physically contiguous memory. The usage should iterate the
- * call of this method until the end of the overall message.
- *
- * The default receiving buffer size should be small enough that it is always
- * processed within the {@link NetlinkUtils#IO_TIMEOUT_MS} timeout.
- */
- public ByteBuffer recvSockDiagResponse(@NonNull final FileDescriptor fd)
- throws ErrnoException, InterruptedIOException {
- return NetlinkUtils.recvMessage(
- fd, NetlinkUtils.DEFAULT_RECV_BUFSIZE, NetlinkUtils.IO_TIMEOUT_MS);
- }
-
- /**
- * Read supported keepalive count for each transport type from overlay resource.
- */
- public int[] getSupportedKeepalives() {
- return KeepaliveUtils.getSupportedKeepalives(mContext);
- }
-
- /**
- * Construct a new Resource from a new ConnectivityResources.
- */
- public Resources newConnectivityResources() {
- final ConnectivityResources resources = new ConnectivityResources(mContext);
- return resources.get();
- }
- }
-
- private void ensureRunningOnHandlerThread() {
- if (mConnectivityServiceHandler.getLooper().getThread() != Thread.currentThread()) {
- throw new IllegalStateException(
- "Not running on handler thread: " + Thread.currentThread().getName());
- }
- }
-
- @VisibleForTesting
- boolean isAnyTcpSocketConnected(int netId) {
- FileDescriptor fd = null;
-
- try {
- fd = mDependencies.createConnectedNetlinkSocket();
-
- // Get network mask
- final MarkMaskParcel parcel = mNetd.getFwmarkForNetwork(netId);
- final int networkMark = (parcel != null) ? parcel.mark : NetlinkUtils.UNKNOWN_MARK;
- final int networkMask = (parcel != null) ? parcel.mask : NetlinkUtils.NULL_MASK;
-
- // Send request for each IP family
- for (final int family : ADDRESS_FAMILIES) {
- if (isAnyTcpSocketConnectedForFamily(fd, family, networkMark, networkMask)) {
- return true;
- }
- }
- } catch (ErrnoException | SocketException | InterruptedIOException | RemoteException e) {
- Log.e(TAG, "Fail to get socket info via netlink.", e);
- } finally {
- SocketUtils.closeSocketQuietly(fd);
- }
-
- return false;
- }
-
- private boolean isAnyTcpSocketConnectedForFamily(FileDescriptor fd, int family, int networkMark,
- int networkMask) throws ErrnoException, InterruptedIOException {
- ensureRunningOnHandlerThread();
- // Build SocketDiag messages and cache it.
- if (mSockDiagMsg.get(family) == null) {
- mSockDiagMsg.put(family, InetDiagMessage.buildInetDiagReqForAliveTcpSockets(family));
- }
- mDependencies.sendRequest(fd, mSockDiagMsg.get(family));
-
- // Iteration limitation as a protection to avoid possible infinite loops.
- // DEFAULT_RECV_BUFSIZE could read more than 20 sockets per time. Max iteration
- // should be enough to go through reasonable TCP sockets in the device.
- final int maxIteration = 100;
- int parsingIteration = 0;
- while (parsingIteration < maxIteration) {
- final ByteBuffer bytes = mDependencies.recvSockDiagResponse(fd);
-
- try {
- while (NetlinkUtils.enoughBytesRemainForValidNlMsg(bytes)) {
- final int startPos = bytes.position();
-
- final int nlmsgLen = bytes.getInt();
- final int nlmsgType = bytes.getShort();
- if (isEndOfMessageOrError(nlmsgType)) return false;
- // TODO: Parse InetDiagMessage to get uid and dst address information to filter
- // socket via NetlinkMessage.parse.
-
- // Skip the header to move to data part.
- bytes.position(startPos + SOCKDIAG_MSG_HEADER_SIZE);
-
- if (isTargetTcpSocket(bytes, nlmsgLen, networkMark, networkMask)) {
- return true;
- }
- }
- } catch (BufferUnderflowException e) {
- // The exception happens in random place in either header position or any data
- // position. Partial bytes from the middle of the byte buffer may not be enough to
- // clarify, so print out the content before the error to possibly prevent printing
- // the whole 8K buffer.
- final int exceptionPos = bytes.position();
- final String hex = HexDump.dumpHexString(bytes.array(), 0, exceptionPos);
- Log.e(TAG, "Unexpected socket info parsing: " + hex, e);
- }
-
- parsingIteration++;
- }
- return false;
- }
-
- private boolean isEndOfMessageOrError(int nlmsgType) {
- return nlmsgType == NLMSG_DONE || nlmsgType != SOCK_DIAG_BY_FAMILY;
- }
-
- private boolean isTargetTcpSocket(@NonNull ByteBuffer bytes, int nlmsgLen, int networkMark,
- int networkMask) {
- final int mark = readSocketDataAndReturnMark(bytes, nlmsgLen);
- return (mark & networkMask) == networkMark;
- }
-
- private int readSocketDataAndReturnMark(@NonNull ByteBuffer bytes, int nlmsgLen) {
- final int nextMsgOffset = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE;
- int mark = NetlinkUtils.INIT_MARK_VALUE;
- // Get socket mark
- // TODO: Add a parsing method in NetlinkMessage.parse to support this to skip the remaining
- // data.
- while (bytes.position() < nextMsgOffset) {
- final StructNlAttr nlattr = StructNlAttr.parse(bytes);
- if (nlattr != null && nlattr.nla_type == NetlinkUtils.INET_DIAG_MARK) {
- mark = nlattr.getValueAsInteger();
- }
- }
- return mark;
- }
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index ff979d8..c15f042 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -1211,18 +1211,6 @@
}
}
- /** Should only be used by unit tests */
- @VisibleForTesting
- public synchronized Set<UidRange> getVpnInterfaceUidRanges(String iface) {
- return mVpnInterfaceUidRanges.get(iface);
- }
-
- /** Should only be used by unit tests */
- @VisibleForTesting
- synchronized Set<UidRange> getVpnLockdownUidRanges() {
- return mVpnLockdownUidRanges.getSet();
- }
-
private synchronized void onSettingChanged() {
// Step1. Update uids allowed to use restricted networks and compute the set of uids to
// update.
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 61b597a..d4b23a3 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -96,6 +96,7 @@
import static com.android.net.module.util.NetworkStackConstants.TEST_CAPTIVE_PORTAL_HTTP_URL;
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;
import static com.android.testutils.MiscAsserts.assertThrows;
@@ -1131,7 +1132,8 @@
final ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
- mContext.registerReceiver(receiver, filter);
+ final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
+ mContext.registerReceiver(receiver, filter, flags);
// Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION.
final Intent intent = new Intent(NETWORK_CALLBACK_ACTION)
@@ -1188,7 +1190,8 @@
final String extraBoolKey = "extra_bool";
firstIntent = PendingIntent.getBroadcast(mContext,
0 /* requestCode */,
- new Intent(broadcastAction).putExtra(extraBoolKey, false),
+ new Intent(broadcastAction).putExtra(extraBoolKey, false)
+ .setPackage(mContext.getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
if (useListen) {
@@ -1201,7 +1204,8 @@
// intent will be updated with the new extras
secondIntent = PendingIntent.getBroadcast(mContext,
0 /* requestCode */,
- new Intent(broadcastAction).putExtra(extraBoolKey, true),
+ new Intent(broadcastAction).putExtra(extraBoolKey, true)
+ .setPackage(mContext.getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
// Because secondIntent.intentFilterEquals the first, the request should be replaced
@@ -1225,7 +1229,8 @@
networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
}
};
- mContext.registerReceiver(receiver, filter);
+ final int flags = SdkLevel.isAtLeastT() ? RECEIVER_EXPORTED : 0;
+ mContext.registerReceiver(receiver, filter, flags);
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
try {
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 2b5c305..b7eb009 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -41,7 +41,9 @@
import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
import android.net.cts.NsdManagerTest.NsdRegistrationRecord.RegistrationEvent.UnregistrationFailed
import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ResolveFailed
+import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ResolveStopped
import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.ServiceResolved
+import android.net.cts.NsdManagerTest.NsdResolveRecord.ResolveEvent.StopResolutionFailed
import android.net.nsd.NsdManager
import android.net.nsd.NsdManager.DiscoveryListener
import android.net.nsd.NsdManager.RegistrationListener
@@ -66,15 +68,6 @@
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
-import org.junit.Assert.assertTrue
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
import java.io.File
import java.net.ServerSocket
import java.nio.charset.StandardCharsets
@@ -86,6 +79,15 @@
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
+import org.junit.After
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
private const val TAG = "NsdManagerTest"
private const val TIMEOUT_MS = 2000L
@@ -182,10 +184,10 @@
val errorCode: Int
) : RegistrationEvent()
- data class ServiceRegistered(override val serviceInfo: NsdServiceInfo)
- : RegistrationEvent()
- data class ServiceUnregistered(override val serviceInfo: NsdServiceInfo)
- : RegistrationEvent()
+ data class ServiceRegistered(override val serviceInfo: NsdServiceInfo) :
+ RegistrationEvent()
+ data class ServiceUnregistered(override val serviceInfo: NsdServiceInfo) :
+ RegistrationEvent()
}
override fun onRegistrationFailed(si: NsdServiceInfo, err: Int) {
@@ -208,11 +210,11 @@
private class NsdDiscoveryRecord(expectedThreadId: Int? = null) :
DiscoveryListener, NsdRecord<NsdDiscoveryRecord.DiscoveryEvent>(expectedThreadId) {
sealed class DiscoveryEvent : NsdEvent {
- data class StartDiscoveryFailed(val serviceType: String, val errorCode: Int)
- : DiscoveryEvent()
+ data class StartDiscoveryFailed(val serviceType: String, val errorCode: Int) :
+ DiscoveryEvent()
- data class StopDiscoveryFailed(val serviceType: String, val errorCode: Int)
- : DiscoveryEvent()
+ data class StopDiscoveryFailed(val serviceType: String, val errorCode: Int) :
+ DiscoveryEvent()
data class DiscoveryStarted(val serviceType: String) : DiscoveryEvent()
data class DiscoveryStopped(val serviceType: String) : DiscoveryEvent()
@@ -259,10 +261,13 @@
private class NsdResolveRecord : ResolveListener,
NsdRecord<NsdResolveRecord.ResolveEvent>() {
sealed class ResolveEvent : NsdEvent {
- data class ResolveFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int)
- : ResolveEvent()
+ data class ResolveFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
+ ResolveEvent()
data class ServiceResolved(val serviceInfo: NsdServiceInfo) : ResolveEvent()
+ data class ResolveStopped(val serviceInfo: NsdServiceInfo) : ResolveEvent()
+ data class StopResolutionFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
+ ResolveEvent()
}
override fun onResolveFailed(si: NsdServiceInfo, err: Int) {
@@ -272,6 +277,14 @@
override fun onServiceResolved(si: NsdServiceInfo) {
add(ServiceResolved(si))
}
+
+ override fun onResolveStopped(si: NsdServiceInfo) {
+ add(ResolveStopped(si))
+ }
+
+ override fun onStopResolutionFailed(si: NsdServiceInfo, err: Int) {
+ add(StopResolutionFailed(si, err))
+ }
}
@Before
@@ -739,6 +752,26 @@
NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER))
}
+ @Test
+ fun testStopServiceResolution() {
+ // This test requires shims supporting U+ APIs (NsdManager.stopServiceResolution)
+ assumeTrue(TestUtils.shouldTestUApis())
+
+ val si = NsdServiceInfo()
+ si.serviceType = this@NsdManagerTest.serviceType
+ si.serviceName = this@NsdManagerTest.serviceName
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ val resolveRecord = NsdResolveRecord()
+ // Try to resolve an unknown service then stop it immediately.
+ // Expected ResolveStopped callback.
+ nsdShim.resolveService(nsdManager, si, { it.run() }, resolveRecord)
+ nsdShim.stopServiceResolution(nsdManager, resolveRecord)
+ val stoppedCb = resolveRecord.expectCallback<ResolveStopped>()
+ assertEquals(si.serviceName, stoppedCb.serviceInfo.serviceName)
+ assertEquals(si.serviceType, stoppedCb.serviceInfo.serviceType)
+ }
+
/**
* Register a service and return its registration record.
*/
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
index 64355ed..9ce0693 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.InetAddresses;
import android.net.Network;
import android.os.Build;
import android.os.Bundle;
@@ -38,6 +39,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
@RunWith(DevSdkIgnoreRunner.class)
@@ -45,6 +47,8 @@
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class NsdServiceInfoTest {
+ private static final InetAddress IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
+ private static final InetAddress IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::");
public final static InetAddress LOCALHOST;
static {
// Because test.
@@ -124,6 +128,7 @@
fullInfo.setServiceType("_kitten._tcp");
fullInfo.setPort(4242);
fullInfo.setHost(LOCALHOST);
+ fullInfo.setHostAddresses(List.of(IPV4_ADDRESS));
fullInfo.setNetwork(new Network(123));
fullInfo.setInterfaceIndex(456);
checkParcelable(fullInfo);
@@ -139,6 +144,7 @@
attributedInfo.setServiceType("_kitten._tcp");
attributedInfo.setPort(4242);
attributedInfo.setHost(LOCALHOST);
+ fullInfo.setHostAddresses(List.of(IPV6_ADDRESS, IPV4_ADDRESS));
attributedInfo.setAttribute("color", "pink");
attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
attributedInfo.setAttribute("adorable", (String) null);
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 17e769c..a2d284b 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -848,6 +848,7 @@
verify(mBroadcastOptionsShim).setDeliveryGroupMatchingKey(
eq(CONNECTIVITY_ACTION),
eq(createDeliveryGroupKeyForConnectivityAction(ni)));
+ verify(mBroadcastOptionsShim).setDeferUntilActive(eq(true));
} catch (UnsupportedApiLevelException e) {
throw new RuntimeException(e);
}
@@ -10881,7 +10882,6 @@
verify(mBpfNetMaps, times(2)).addUidInterfaceRules(eq("tun0"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID);
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
- assertTrue(mService.mPermissionMonitor.getVpnInterfaceUidRanges("tun0").equals(vpnRange));
mMockVpn.disconnect();
waitForIdle();
@@ -10889,7 +10889,6 @@
// Disconnected VPN should have interface rules removed
verify(mBpfNetMaps).removeUidInterfaceRules(uidCaptor.capture());
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
- assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges("tun0"));
}
private void checkInterfaceFilteringRuleWithNullInterface(final LinkProperties lp,
@@ -10914,8 +10913,6 @@
assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID, VPN_UID);
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID, VPN_UID);
}
- assertEquals(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */),
- vpnRange);
mMockVpn.disconnect();
waitForIdle();
@@ -10927,7 +10924,6 @@
} else {
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID, VPN_UID);
}
- assertNull(mService.mPermissionMonitor.getVpnInterfaceUidRanges(null /* iface */));
} else {
// Before T, rules are not configured for null interface.
verify(mBpfNetMaps, never()).addUidInterfaceRules(any(), any());
@@ -15760,6 +15756,39 @@
}
@Test
+ public void testProfileNetworkPreferenceBlocking_addUser() throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
+
+ // Only one network
+ mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellAgent.connect(true);
+
+ // Verify uid ranges 0~99999 are allowed
+ final ArraySet<UidRange> allowedRanges = new ArraySet<>();
+ allowedRanges.add(PRIMARY_UIDRANGE);
+ final NativeUidRangeConfig config1User = new NativeUidRangeConfig(
+ mCellAgent.getNetwork().netId,
+ toUidRangeStableParcels(allowedRanges),
+ 0 /* subPriority */);
+ inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[] { config1User });
+
+ doReturn(asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE))
+ .when(mUserManager).getUserHandles(anyBoolean());
+ final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(SECONDARY_USER));
+ processBroadcast(addedIntent);
+
+ // Make sure the allow list has been updated.
+ allowedRanges.add(UidRange.createForUser(SECONDARY_USER_HANDLE));
+ final NativeUidRangeConfig config2Users = new NativeUidRangeConfig(
+ mCellAgent.getNetwork().netId,
+ toUidRangeStableParcels(allowedRanges),
+ 0 /* subPriority */);
+ inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[] { config2Users });
+ }
+
+ @Test
public void testProfileNetworkPreferenceBlocking_changePreference() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index a1c865f..98a8ed2 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -16,16 +16,22 @@
package com.android.server;
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
+import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
import static com.android.testutils.ContextUtils.mockService;
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
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 static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
@@ -45,7 +51,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.net.INetd;
-import android.net.InetAddresses;
import android.net.Network;
import android.net.mdns.aidl.DiscoveryInfo;
import android.net.mdns.aidl.GetAddressInfo;
@@ -67,11 +72,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.server.NsdService.Dependencies;
+import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
import com.android.server.connectivity.mdns.MdnsServiceInfo;
@@ -91,8 +98,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
// TODOs:
@@ -111,6 +120,8 @@
private static final String DOMAIN_NAME = "mytestdevice.local";
private static final int PORT = 2201;
private static final int IFACE_IDX_ANY = 0;
+ private static final String IPV4_ADDRESS = "192.0.2.0";
+ private static final String IPV6_ADDRESS = "2001:db8::";
// Records INsdManagerCallback created when NsdService#connect is called.
// Only accessed on the test thread, since NsdService#connect is called by the NsdManager
@@ -124,6 +135,7 @@
@Mock MDnsManager mMockMDnsM;
@Mock Dependencies mDeps;
@Mock MdnsDiscoveryManager mDiscoveryManager;
+ @Mock MdnsAdvertiser mAdvertiser;
@Mock MdnsSocketProvider mSocketProvider;
HandlerThread mThread;
TestHandler mHandler;
@@ -394,13 +406,42 @@
final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
assertEquals(SERVICE_NAME, resolvedService.getServiceName());
assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType());
- assertEquals(InetAddresses.parseNumericAddress(serviceAddress), resolvedService.getHost());
+ assertEquals(parseNumericAddress(serviceAddress), resolvedService.getHost());
assertEquals(servicePort, resolvedService.getPort());
assertNull(resolvedService.getNetwork());
assertEquals(interfaceIdx, resolvedService.getInterfaceIndex());
}
@Test
+ public void testDiscoverOnBlackholeNetwork() throws Exception {
+ final NsdManager client = connectClient(mService);
+ final DiscoveryListener discListener = mock(DiscoveryListener.class);
+ client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
+ waitForIdle();
+
+ final IMDnsEventListener eventListener = getEventListener();
+ final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE),
+ eq(0) /* interfaceIdx */);
+ // NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
+ // this needs to use a timeout
+ verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
+
+ final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
+ discIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_FOUND,
+ SERVICE_NAME,
+ SERVICE_TYPE,
+ DOMAIN_NAME,
+ 123 /* interfaceIdx */,
+ INetd.DUMMY_NET_ID); // netId of the blackhole network
+ eventListener.onServiceDiscoveryStatus(discoveryInfo);
+ waitForIdle();
+
+ verify(discListener, never()).onServiceFound(any());
+ }
+
+ @Test
public void testServiceRegistrationSuccessfulAndFailed() throws Exception {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -567,6 +608,222 @@
anyInt()/* interfaceIdx */);
}
+ @Test
+ public void testStopServiceResolution() {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ final ResolveListener resolveListener = mock(ResolveListener.class);
+ client.resolveService(request, resolveListener);
+ waitForIdle();
+
+ final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+ eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+ final int resolveId = resolvIdCaptor.getValue();
+ client.stopServiceResolution(resolveListener);
+ waitForIdle();
+
+ verify(mMockMDnsM).stopOperation(resolveId);
+ verify(resolveListener, timeout(TIMEOUT_MS)).onResolveStopped(argThat(ns ->
+ request.getServiceName().equals(ns.getServiceName())
+ && request.getServiceType().equals(ns.getServiceType())));
+ }
+
+ @Test
+ public void testStopResolutionFailed() {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ final ResolveListener resolveListener = mock(ResolveListener.class);
+ client.resolveService(request, resolveListener);
+ waitForIdle();
+
+ final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+ eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+ final int resolveId = resolvIdCaptor.getValue();
+ doReturn(false).when(mMockMDnsM).stopOperation(anyInt());
+ client.stopServiceResolution(resolveListener);
+ waitForIdle();
+
+ verify(mMockMDnsM).stopOperation(resolveId);
+ verify(resolveListener, timeout(TIMEOUT_MS)).onStopResolutionFailed(argThat(ns ->
+ request.getServiceName().equals(ns.getServiceName())
+ && request.getServiceType().equals(ns.getServiceType())),
+ eq(FAILURE_OPERATION_NOT_RUNNING));
+ }
+
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testStopResolutionDuringGettingAddress() throws RemoteException {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ final ResolveListener resolveListener = mock(ResolveListener.class);
+ client.resolveService(request, resolveListener);
+ waitForIdle();
+
+ final IMDnsEventListener eventListener = getEventListener();
+ final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+ eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+ // Resolve service successfully.
+ final ResolutionInfo resolutionInfo = new ResolutionInfo(
+ resolvIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_RESOLVED,
+ null /* serviceName */,
+ null /* serviceType */,
+ null /* domain */,
+ SERVICE_FULL_NAME,
+ DOMAIN_NAME,
+ PORT,
+ new byte[0] /* txtRecord */,
+ IFACE_IDX_ANY);
+ doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
+ eventListener.onServiceResolutionStatus(resolutionInfo);
+ waitForIdle();
+
+ final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
+ eq(IFACE_IDX_ANY));
+
+ final int getAddrId = getAddrIdCaptor.getValue();
+ client.stopServiceResolution(resolveListener);
+ waitForIdle();
+
+ verify(mMockMDnsM).stopOperation(getAddrId);
+ verify(resolveListener, timeout(TIMEOUT_MS)).onResolveStopped(argThat(ns ->
+ request.getServiceName().equals(ns.getServiceName())
+ && request.getServiceType().equals(ns.getServiceType())));
+ }
+
+ private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
+ String serviceType, String address, int port, int interfaceIndex, Network network) {
+ assertEquals(serviceName, info.getServiceName());
+ assertEquals(serviceType, info.getServiceType());
+ assertTrue(info.getHostAddresses().contains(parseNumericAddress(address)));
+ assertEquals(port, info.getPort());
+ assertEquals(network, info.getNetwork());
+ assertEquals(interfaceIndex, info.getInterfaceIndex());
+ }
+
+ @Test
+ public void testRegisterAndUnregisterServiceInfoCallback() throws RemoteException {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
+ NsdManager.ServiceInfoCallback.class);
+ client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
+ waitForIdle();
+
+ final IMDnsEventListener eventListener = getEventListener();
+ final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+ eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+ // Resolve service successfully.
+ final ResolutionInfo resolutionInfo = new ResolutionInfo(
+ resolvIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_RESOLVED,
+ null /* serviceName */,
+ null /* serviceType */,
+ null /* domain */,
+ SERVICE_FULL_NAME,
+ DOMAIN_NAME,
+ PORT,
+ new byte[0] /* txtRecord */,
+ IFACE_IDX_ANY);
+ doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt());
+ eventListener.onServiceResolutionStatus(resolutionInfo);
+ waitForIdle();
+
+ final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
+ eq(IFACE_IDX_ANY));
+
+ // First address info
+ final String v4Address = "192.0.2.1";
+ final String v6Address = "2001:db8::";
+ final GetAddressInfo addressInfo1 = new GetAddressInfo(
+ getAddrIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
+ SERVICE_FULL_NAME,
+ v4Address,
+ IFACE_IDX_ANY,
+ 999 /* netId */);
+ eventListener.onGettingServiceAddressStatus(addressInfo1);
+ waitForIdle();
+
+ final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
+ ArgumentCaptor.forClass(NsdServiceInfo.class);
+ verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
+ .onServiceUpdated(updateInfoCaptor.capture());
+ verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME,
+ "." + SERVICE_TYPE, v4Address, PORT, IFACE_IDX_ANY, new Network(999));
+
+ // Second address info
+ final GetAddressInfo addressInfo2 = new GetAddressInfo(
+ getAddrIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
+ SERVICE_FULL_NAME,
+ v6Address,
+ IFACE_IDX_ANY,
+ 999 /* netId */);
+ eventListener.onGettingServiceAddressStatus(addressInfo2);
+ waitForIdle();
+
+ verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2))
+ .onServiceUpdated(updateInfoCaptor.capture());
+ verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(1) /* info */, SERVICE_NAME,
+ "." + SERVICE_TYPE, v6Address, PORT, IFACE_IDX_ANY, new Network(999));
+
+ client.unregisterServiceInfoCallback(serviceInfoCallback);
+ waitForIdle();
+
+ verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered();
+ }
+
+ @Test
+ public void testRegisterServiceCallbackFailed() throws Exception {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ final NsdManager.ServiceInfoCallback subscribeListener = mock(
+ NsdManager.ServiceInfoCallback.class);
+ client.registerServiceInfoCallback(request, Runnable::run, subscribeListener);
+ waitForIdle();
+
+ final IMDnsEventListener eventListener = getEventListener();
+ final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
+ eq("local.") /* domain */, eq(IFACE_IDX_ANY));
+
+ // Fail to resolve service.
+ final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
+ resolvIdCaptor.getValue(),
+ IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
+ null /* serviceName */,
+ null /* serviceType */,
+ null /* domain */,
+ null /* serviceFullName */,
+ null /* domainName */,
+ 0 /* port */,
+ new byte[0] /* txtRecord */,
+ IFACE_IDX_ANY);
+ eventListener.onServiceResolutionStatus(resolutionFailedInfo);
+ verify(subscribeListener, timeout(TIMEOUT_MS))
+ .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS));
+ }
+
+ @Test
+ public void testUnregisterNotRegisteredCallback() {
+ final NsdManager client = connectClient(mService);
+ final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
+ NsdManager.ServiceInfoCallback.class);
+
+ assertThrows(IllegalArgumentException.class, () ->
+ client.unregisterServiceInfoCallback(serviceInfoCallback));
+ }
+
private void makeServiceWithMdnsDiscoveryManagerEnabled() {
doReturn(true).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
doReturn(mDiscoveryManager).when(mDeps).makeMdnsDiscoveryManager(any(), any());
@@ -577,6 +834,16 @@
verify(mDeps).makeMdnsSocketProvider(any(), any());
}
+ private void makeServiceWithMdnsAdvertiserEnabled() {
+ doReturn(true).when(mDeps).isMdnsAdvertiserEnabled(any(Context.class));
+ doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any());
+ doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any());
+
+ mService = makeService();
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), any());
+ verify(mDeps).makeMdnsSocketProvider(any(), any());
+ }
+
@Test
public void testMdnsDiscoveryManagerFeature() {
// Create NsdService w/o feature enabled.
@@ -613,8 +880,8 @@
List.of(), /* subtypes */
new String[] {"android", "local"}, /* hostName */
12345, /* port */
- "192.0.2.0", /* ipv4Address */
- "2001:db8::", /* ipv6Address */
+ IPV4_ADDRESS,
+ IPV6_ADDRESS,
List.of(), /* textStrings */
List.of(), /* textEntries */
1234, /* interfaceIndex */
@@ -682,6 +949,157 @@
.onStartDiscoveryFailed(serviceTypeWithoutTcpOrUdpEnding, FAILURE_INTERNAL_ERROR);
}
+ @Test
+ public void testResolutionWithMdnsDiscoveryManager() throws UnknownHostException {
+ makeServiceWithMdnsDiscoveryManagerEnabled();
+
+ final NsdManager client = connectClient(mService);
+ final ResolveListener resolveListener = mock(ResolveListener.class);
+ final Network network = new Network(999);
+ final String serviceType = "_nsd._service._tcp";
+ final String constructedServiceType = "_nsd._sub._service._tcp.local";
+ final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
+ ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
+ request.setNetwork(network);
+ client.resolveService(request, resolveListener);
+ waitForIdle();
+ verify(mSocketProvider).startMonitoringSockets();
+ verify(mDiscoveryManager).registerListener(eq(constructedServiceType),
+ listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
+
+ final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
+ final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
+ SERVICE_NAME,
+ constructedServiceType.split("\\."),
+ List.of(), /* subtypes */
+ new String[]{"android", "local"}, /* hostName */
+ PORT,
+ IPV4_ADDRESS,
+ IPV6_ADDRESS,
+ List.of() /* textStrings */,
+ List.of(MdnsServiceInfo.TextEntry.fromBytes(new byte[]{
+ 'k', 'e', 'y', '=', (byte) 0xFF, (byte) 0xFE})) /* textEntries */,
+ 1234,
+ network);
+
+ // Verify onServiceFound callback
+ listener.onServiceFound(mdnsServiceInfo);
+ final ArgumentCaptor<NsdServiceInfo> infoCaptor =
+ ArgumentCaptor.forClass(NsdServiceInfo.class);
+ verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture());
+ final NsdServiceInfo info = infoCaptor.getValue();
+ assertEquals(SERVICE_NAME, info.getServiceName());
+ assertEquals("." + serviceType, info.getServiceType());
+ assertEquals(PORT, info.getPort());
+ assertTrue(info.getAttributes().containsKey("key"));
+ assertEquals(1, info.getAttributes().size());
+ assertArrayEquals(new byte[]{(byte) 0xFF, (byte) 0xFE}, info.getAttributes().get("key"));
+ assertEquals(parseNumericAddress(IPV4_ADDRESS), info.getHost());
+ assertEquals(network, info.getNetwork());
+
+ // Verify the listener has been unregistered.
+ verify(mDiscoveryManager, timeout(TIMEOUT_MS))
+ .unregisterListener(eq(constructedServiceType), any());
+ verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).stopMonitoringSockets();
+ }
+
+ @Test
+ public void testAdvertiseWithMdnsAdvertiser() {
+ makeServiceWithMdnsAdvertiserEnabled();
+
+ final NsdManager client = connectClient(mService);
+ final RegistrationListener regListener = mock(RegistrationListener.class);
+ // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
+ final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
+ ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+
+ final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ regInfo.setHost(parseNumericAddress("192.0.2.123"));
+ regInfo.setPort(12345);
+ regInfo.setAttribute("testattr", "testvalue");
+ regInfo.setNetwork(new Network(999));
+
+ client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
+ waitForIdle();
+ verify(mSocketProvider).startMonitoringSockets();
+ final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mAdvertiser).addService(idCaptor.capture(), argThat(info ->
+ matches(info, regInfo)));
+
+ // Verify onServiceRegistered callback
+ final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
+ cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo);
+
+ verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(argThat(info -> matches(info,
+ new NsdServiceInfo(regInfo.getServiceName(), null))));
+
+ client.unregisterService(regListener);
+ waitForIdle();
+ verify(mAdvertiser).removeService(idCaptor.getValue());
+ verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
+ argThat(info -> matches(info, regInfo)));
+ verify(mSocketProvider, timeout(TIMEOUT_MS)).stopMonitoringSockets();
+ }
+
+ @Test
+ public void testAdvertiseWithMdnsAdvertiser_FailedWithInvalidServiceType() {
+ makeServiceWithMdnsAdvertiserEnabled();
+
+ final NsdManager client = connectClient(mService);
+ final RegistrationListener regListener = mock(RegistrationListener.class);
+ // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
+ final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
+ ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+
+ final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, "invalid_type");
+ regInfo.setHost(parseNumericAddress("192.0.2.123"));
+ regInfo.setPort(12345);
+ regInfo.setAttribute("testattr", "testvalue");
+ regInfo.setNetwork(new Network(999));
+
+ client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
+ waitForIdle();
+ verify(mAdvertiser, never()).addService(anyInt(), any());
+
+ verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
+ argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
+ }
+
+ @Test
+ public void testAdvertiseWithMdnsAdvertiser_LongServiceName() {
+ makeServiceWithMdnsAdvertiserEnabled();
+
+ final NsdManager client = connectClient(mService);
+ final RegistrationListener regListener = mock(RegistrationListener.class);
+ // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
+ final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
+ ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+
+ final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE);
+ regInfo.setHost(parseNumericAddress("192.0.2.123"));
+ regInfo.setPort(12345);
+ regInfo.setAttribute("testattr", "testvalue");
+ regInfo.setNetwork(new Network(999));
+
+ client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
+ waitForIdle();
+ final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
+ // Service name is truncated to 63 characters
+ verify(mAdvertiser).addService(idCaptor.capture(),
+ argThat(info -> info.getServiceName().equals("a".repeat(63))));
+
+ // Verify onServiceRegistered callback
+ final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
+ cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo);
+
+ verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(
+ argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null))));
+ }
+
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
}
@@ -726,6 +1144,19 @@
verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).stopDaemon();
}
+ /**
+ * Return true if two service info are the same.
+ *
+ * Useful for argument matchers as {@link NsdServiceInfo} does not implement equals.
+ */
+ private boolean matches(NsdServiceInfo a, NsdServiceInfo b) {
+ return Objects.equals(a.getServiceName(), b.getServiceName())
+ && Objects.equals(a.getServiceType(), b.getServiceType())
+ && Objects.equals(a.getHost(), b.getHost())
+ && Objects.equals(a.getNetwork(), b.getNetwork())
+ && Objects.equals(a.getAttributes(), b.getAttributes());
+ }
+
public static class TestHandler extends Handler {
public Message lastMessage;
diff --git a/tests/unit/java/com/android/server/VpnManagerServiceTest.java b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
index c8a93a6..deb56ef 100644
--- a/tests/unit/java/com/android/server/VpnManagerServiceTest.java
+++ b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
@@ -131,6 +131,11 @@
Vpn vpn, VpnProfile profile) {
return mLockdownVpnTracker;
}
+
+ @Override
+ public @UserIdInt int getMainUserId() {
+ return UserHandle.USER_SYSTEM;
+ }
}
@Before
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
similarity index 86%
rename from tests/unit/java/com/android/server/connectivity/KeepaliveTrackerTest.java
rename to tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index b55ee67..6c29d6e 100644
--- a/tests/unit/java/com/android/server/connectivity/KeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -24,14 +24,12 @@
import static org.mockito.Mockito.doReturn;
import android.content.Context;
-import android.content.res.Resources;
import android.net.INetd;
import android.net.MarkMaskParcel;
import android.os.Build;
import android.os.HandlerThread;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.connectivity.resources.R;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -48,21 +46,19 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-public class KeepaliveTrackerTest {
- private static final int[] TEST_SUPPORTED_KEEPALIVES = {1, 3, 0, 0, 0, 0, 0, 0, 0};
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+public class AutomaticOnOffKeepaliveTrackerTest {
private static final int TEST_NETID = 0xA85;
private static final int TEST_NETID_FWMARK = 0x0A85;
private static final int OTHER_NETID = 0x1A85;
private static final int NETID_MASK = 0xffff;
- private static final int SUPPORTED_SLOT_COUNT = 2;
- private KeepaliveTracker mKeepaliveTracker;
+ private AutomaticOnOffKeepaliveTracker mAOOKeepaliveTracker;
private HandlerThread mHandlerThread;
@Mock INetd mNetd;
- @Mock KeepaliveTracker.Dependencies mDependencies;
+ @Mock AutomaticOnOffKeepaliveTracker.Dependencies mDependencies;
@Mock Context mCtx;
- @Mock Resources mResources;
+ @Mock KeepaliveTracker mKeepaliveTracker;
// Hexadecimal representation of a SOCK_DIAG response with tcp info.
private static final String SOCK_DIAG_TCP_INET_HEX =
@@ -169,51 +165,43 @@
doReturn(makeMarkMaskParcel(NETID_MASK, TEST_NETID_FWMARK)).when(mNetd)
.getFwmarkForNetwork(TEST_NETID);
- doReturn(TEST_SUPPORTED_KEEPALIVES).when(mDependencies).getSupportedKeepalives();
- doReturn(mResources).when(mDependencies).newConnectivityResources();
- mockResource();
doNothing().when(mDependencies).sendRequest(any(), any());
mHandlerThread = new HandlerThread("KeepaliveTrackerTest");
mHandlerThread.start();
-
- mKeepaliveTracker = new KeepaliveTracker(mCtx, mHandlerThread.getThreadHandler(),
- mDependencies);
- }
-
- private void mockResource() {
- doReturn(SUPPORTED_SLOT_COUNT).when(mResources).getInteger(
- R.integer.config_reservedPrivilegedKeepaliveSlots);
- doReturn(SUPPORTED_SLOT_COUNT).when(mResources).getInteger(
- R.integer.config_allowedUnprivilegedKeepalivePerUid);
+ doReturn(mKeepaliveTracker).when(mDependencies).newKeepaliveTracker(
+ mCtx, mHandlerThread.getThreadHandler());
+ doReturn(true).when(mDependencies).isFeatureEnabled(any());
+ mAOOKeepaliveTracker = new AutomaticOnOffKeepaliveTracker(
+ mCtx, mHandlerThread.getThreadHandler(), mDependencies);
}
@Test
public void testIsAnyTcpSocketConnected_runOnNonHandlerThread() throws Exception {
setupResponseWithSocketExisting();
assertThrows(IllegalStateException.class,
- () -> mKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID));
+ () -> mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID));
}
@Test
public void testIsAnyTcpSocketConnected_withTargetNetId() throws Exception {
setupResponseWithSocketExisting();
mHandlerThread.getThreadHandler().post(
- () -> assertTrue(mKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
+ () -> assertTrue(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
}
@Test
public void testIsAnyTcpSocketConnected_withIncorrectNetId() throws Exception {
setupResponseWithSocketExisting();
mHandlerThread.getThreadHandler().post(
- () -> assertFalse(mKeepaliveTracker.isAnyTcpSocketConnected(OTHER_NETID)));
+ () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(OTHER_NETID)));
}
@Test
public void testIsAnyTcpSocketConnected_noSocketExists() throws Exception {
setupResponseWithoutSocketExisting();
mHandlerThread.getThreadHandler().post(
- () -> assertFalse(mKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
+ () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
}
private void setupResponseWithSocketExisting() throws Exception {
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 8076edb..cf02e3a 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -46,7 +46,6 @@
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.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
@@ -844,7 +843,6 @@
// When VPN is disconnected, expect rules to be torn down
mPermissionMonitor.onVpnUidRangesRemoved(ifName, vpnRange2, VPN_UID);
verify(mBpfNetMaps).removeUidInterfaceRules(aryEq(new int[] {MOCK_UID12}));
- assertNull(mPermissionMonitor.getVpnInterfaceUidRanges(ifName));
}
@Test
@@ -915,7 +913,6 @@
verify(mBpfNetMaps, times(2)).updateUidLockdownRule(anyInt(), eq(true) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, true /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(VPN_UID, true /* add */);
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -924,7 +921,6 @@
verify(mBpfNetMaps, times(2)).updateUidLockdownRule(anyInt(), eq(false) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, false /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(VPN_UID, false /* add */);
- assertTrue(mPermissionMonitor.getVpnLockdownUidRanges().isEmpty());
}
@Test
@@ -944,7 +940,6 @@
mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRange);
verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(true) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, true /* add */);
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -952,7 +947,6 @@
// already has the rule
mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRange);
verify(mBpfNetMaps, never()).updateUidLockdownRule(anyInt(), anyBoolean());
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -960,7 +954,6 @@
// the range 2 times.
mPermissionMonitor.updateVpnLockdownUidRanges(false /* add */, lockdownRange);
verify(mBpfNetMaps, never()).updateUidLockdownRule(anyInt(), anyBoolean());
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -969,7 +962,6 @@
mPermissionMonitor.updateVpnLockdownUidRanges(false /* add */, lockdownRange);
verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(false) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, false /* add */);
- assertTrue(mPermissionMonitor.getVpnLockdownUidRanges().isEmpty());
}
@Test
@@ -990,7 +982,6 @@
mPermissionMonitor.updateVpnLockdownUidRanges(true /* add */, lockdownRangeDuplicates);
verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(true) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, true /* add */);
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -998,7 +989,6 @@
// ranges we added contains duplicated uid ranges.
mPermissionMonitor.updateVpnLockdownUidRanges(false /* add */, lockdownRange);
verify(mBpfNetMaps, never()).updateUidLockdownRule(anyInt(), anyBoolean());
- assertEquals(mPermissionMonitor.getVpnLockdownUidRanges(), Set.of(lockdownRange));
reset(mBpfNetMaps);
@@ -1006,7 +996,6 @@
mPermissionMonitor.updateVpnLockdownUidRanges(false /* add */, lockdownRange);
verify(mBpfNetMaps).updateUidLockdownRule(anyInt(), eq(false) /* add */);
verify(mBpfNetMaps).updateUidLockdownRule(MOCK_UID11, false /* add */);
- assertTrue(mPermissionMonitor.getVpnLockdownUidRanges().isEmpty());
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index e2babb1..1febe6d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -38,6 +38,7 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.argThat
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -161,6 +162,60 @@
verify(socketProvider).unrequestSocket(socketCb)
}
+ @Test
+ fun testAddService_Conflicts() {
+ val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps)
+ postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1) }
+
+ val oneNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
+ verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), oneNetSocketCbCaptor.capture())
+ val oneNetSocketCb = oneNetSocketCbCaptor.value
+
+ // Register a service with the same name on all networks (name conflict)
+ postSync { advertiser.addService(SERVICE_ID_2, ALL_NETWORKS_SERVICE) }
+ val allNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
+ verify(socketProvider).requestSocket(eq(null), allNetSocketCbCaptor.capture())
+ val allNetSocketCb = allNetSocketCbCaptor.value
+
+ // Callbacks for matching network and all networks both get the socket
+ postSync {
+ oneNetSocketCb.onSocketCreated(TEST_NETWORK_1, mockSocket1, listOf(TEST_LINKADDR))
+ allNetSocketCb.onSocketCreated(TEST_NETWORK_1, mockSocket1, listOf(TEST_LINKADDR))
+ }
+
+ val expectedRenamed = NsdServiceInfo(
+ "${ALL_NETWORKS_SERVICE.serviceName} (2)", ALL_NETWORKS_SERVICE.serviceType).apply {
+ port = ALL_NETWORKS_SERVICE.port
+ host = ALL_NETWORKS_SERVICE.host
+ network = ALL_NETWORKS_SERVICE.network
+ }
+
+ val intAdvCbCaptor = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java)
+ verify(mockDeps).makeAdvertiser(eq(mockSocket1), eq(listOf(TEST_LINKADDR)),
+ eq(thread.looper), any(), intAdvCbCaptor.capture())
+ verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
+ argThat { it.matches(SERVICE_1) })
+ verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_2),
+ argThat { it.matches(expectedRenamed) })
+
+ doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
+ postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
+ mockInterfaceAdvertiser1, SERVICE_ID_1) }
+ verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
+
+ doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_2)
+ postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
+ mockInterfaceAdvertiser1, SERVICE_ID_2) }
+ verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_2),
+ argThat { it.matches(expectedRenamed) })
+
+ postSync { oneNetSocketCb.onInterfaceDestroyed(TEST_NETWORK_1, mockSocket1) }
+ postSync { allNetSocketCb.onInterfaceDestroyed(TEST_NETWORK_1, mockSocket1) }
+
+ // destroyNow can be called multiple times
+ verify(mockInterfaceAdvertiser1, atLeastOnce()).destroyNow()
+ }
+
private fun postSync(r: () -> Unit) {
handler.post(r)
handler.waitForIdle(TIMEOUT_MS)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
index 650607d..6c3f729 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
@@ -79,7 +79,7 @@
@Test
fun testAnnounce() {
- val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
@Suppress("UNCHECKED_CAST")
val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
as MdnsPacketRepeater.PacketRepeaterCallback<BaseAnnouncementInfo>
@@ -91,7 +91,7 @@
scapy.raw(scapy.dns_compress(scapy.DNS(rd=0, qr=1, aa=1,
qd = None,
an =
- scapy.DNSRR(type='PTR', rrname='123.0.2.192.in-addr.arpa.', rdata='Android.local',
+ scapy.DNSRR(type='PTR', rrname='123.2.0.192.in-addr.arpa.', rdata='Android.local',
rclass=0x8001, ttl=120) /
scapy.DNSRR(type='PTR',
rrname='3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
@@ -111,8 +111,8 @@
scapy.DNSRR(type='AAAA', rrname='Android.local', rclass=0x8001, rdata='2001:db8::456',
ttl=120),
ar =
- scapy.DNSRRNSEC(rrname='123.0.2.192.in-addr.arpa.', rclass=0x8001, ttl=120,
- nextname='123.0.2.192.in-addr.arpa.', typebitmaps=[12]) /
+ scapy.DNSRRNSEC(rrname='123.2.0.192.in-addr.arpa.', rclass=0x8001, ttl=120,
+ nextname='123.2.0.192.in-addr.arpa.', typebitmaps=[12]) /
scapy.DNSRRNSEC(
rrname='3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa',
rclass=0x8001, ttl=120,
@@ -131,7 +131,7 @@
typebitmaps=[1, 28]))
)).hex().upper()
*/
- val expected = "00008400000000090000000503313233013001320331393207696E2D61646472046172706" +
+ val expected = "00008400000000090000000503313233013201300331393207696E2D61646472046172706" +
"100000C800100000078000F07416E64726F6964056C6F63616C00013301320131013001300130013" +
"00130013001300130013001300130013001300130013001300130013001300130013001380142014" +
"40130013101300130013203697036C020000C8001000000780002C030013601350134C045000C800" +
@@ -149,7 +149,7 @@
val v4Addr = parseNumericAddress("192.0.2.123")
val v6Addr1 = parseNumericAddress("2001:DB8::123")
val v6Addr2 = parseNumericAddress("2001:DB8::456")
- val v4AddrRev = arrayOf("123", "0", "2", "192", "in-addr", "arpa")
+ val v4AddrRev = getReverseDnsAddress(v4Addr)
val v6Addr1Rev = getReverseDnsAddress(v6Addr1)
val v6Addr2Rev = getReverseDnsAddress(v6Addr2)
@@ -254,7 +254,10 @@
verify(socket, atLeast(i + 1)).send(any())
val now = SystemClock.elapsedRealtime()
assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY)
- assertTrue(now < timeStart + startDelay + (i + 1) * FIRST_ANNOUNCES_DELAY)
+ // Loops can be much slower than the expected timing (>100ms delay), use
+ // TEST_TIMEOUT_MS as tolerance.
+ assertTrue(now < timeStart + startDelay + (i + 1) * FIRST_ANNOUNCES_DELAY +
+ TEST_TIMEOUT_MS)
}
// Subsequent announces should happen quickly (NEXT_ANNOUNCES_DELAY)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
index 2cb0850..4a806b1 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -21,6 +21,7 @@
import android.net.nsd.NsdServiceInfo
import android.os.Build
import android.os.HandlerThread
+import com.android.net.module.util.HexDump
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo
import com.android.server.connectivity.mdns.MdnsAnnouncer.ExitAnnouncementInfo
@@ -30,6 +31,10 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.waitForIdle
+import java.net.InetSocketAddress
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -37,8 +42,10 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -67,13 +74,18 @@
private val replySender = mock(MdnsReplySender::class.java)
private val announcer = mock(MdnsAnnouncer::class.java)
private val prober = mock(MdnsProber::class.java)
+ @Suppress("UNCHECKED_CAST")
private val probeCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
as ArgumentCaptor<PacketRepeaterCallback<ProbingInfo>>
+ @Suppress("UNCHECKED_CAST")
private val announceCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
as ArgumentCaptor<PacketRepeaterCallback<BaseAnnouncementInfo>>
+ private val packetHandlerCaptor = ArgumentCaptor.forClass(
+ MulticastPacketReader.PacketHandler::class.java)
private val probeCb get() = probeCbCaptor.value
private val announceCb get() = announceCbCaptor.value
+ private val packetHandler get() = packetHandlerCaptor.value
private val advertiser by lazy {
MdnsInterfaceAdvertiser(LOG_TAG, socket, TEST_ADDRS, thread.looper, TEST_BUFFER, cb, deps)
@@ -82,9 +94,9 @@
@Before
fun setUp() {
doReturn(repository).`when`(deps).makeRecordRepository(any())
- doReturn(replySender).`when`(deps).makeReplySender(any(), any(), any())
- doReturn(announcer).`when`(deps).makeMdnsAnnouncer(any(), any(), any(), any())
- doReturn(prober).`when`(deps).makeMdnsProber(any(), any(), any(), any())
+ doReturn(replySender).`when`(deps).makeReplySender(anyString(), any(), any(), any())
+ doReturn(announcer).`when`(deps).makeMdnsAnnouncer(anyString(), any(), any(), any())
+ doReturn(prober).`when`(deps).makeMdnsProber(anyString(), any(), any(), any())
val knownServices = mutableSetOf<Int>()
doAnswer { inv ->
@@ -104,6 +116,7 @@
thread.start()
advertiser.start()
+ verify(socket).addPacketHandler(packetHandlerCaptor.capture())
verify(deps).makeMdnsProber(any(), any(), any(), probeCbCaptor.capture())
verify(deps).makeMdnsAnnouncer(any(), any(), any(), announceCbCaptor.capture())
}
@@ -157,6 +170,92 @@
verify(announcer, times(1)).stop(TEST_SERVICE_ID_1)
}
+ @Test
+ fun testReplyToQuery() {
+ addServiceAndFinishProbing(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+
+ val mockReply = mock(MdnsRecordRepository.ReplyInfo::class.java)
+ doReturn(mockReply).`when`(repository).getReply(any(), any())
+
+ // Query obtained with:
+ // scapy.raw(scapy.DNS(
+ // qd = scapy.DNSQR(qtype='PTR', qname='_testservice._tcp.local'))
+ // ).hex().upper()
+ val query = HexDump.hexStringToByteArray(
+ "0000010000010000000000000C5F7465737473657276696365045F746370056C6F63616C00000C0001"
+ )
+ val src = InetSocketAddress(parseNumericAddress("2001:db8::456"), MdnsConstants.MDNS_PORT)
+ packetHandler.handlePacket(query, query.size, src)
+
+ val packetCaptor = ArgumentCaptor.forClass(MdnsPacket::class.java)
+ verify(repository).getReply(packetCaptor.capture(), eq(src))
+
+ packetCaptor.value.let {
+ assertEquals(1, it.questions.size)
+ assertEquals(0, it.answers.size)
+ assertEquals(0, it.authorityRecords.size)
+ assertEquals(0, it.additionalRecords.size)
+
+ assertTrue(it.questions[0] is MdnsPointerRecord)
+ assertContentEquals(arrayOf("_testservice", "_tcp", "local"), it.questions[0].name)
+ }
+
+ verify(replySender).queueReply(mockReply)
+ }
+
+ @Test
+ fun testConflict() {
+ addServiceAndFinishProbing(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ doReturn(setOf(TEST_SERVICE_ID_1)).`when`(repository).getConflictingServices(any())
+
+ // Reply obtained with:
+ // scapy.raw(scapy.DNS(
+ // qd = None,
+ // an = scapy.DNSRR(type='TXT', rrname='_testservice._tcp.local'))
+ // ).hex().upper()
+ val query = HexDump.hexStringToByteArray("0000010000000001000000000C5F7465737473657276696" +
+ "365045F746370056C6F63616C0000100001000000000000")
+ val src = InetSocketAddress(parseNumericAddress("2001:db8::456"), MdnsConstants.MDNS_PORT)
+ packetHandler.handlePacket(query, query.size, src)
+
+ val packetCaptor = ArgumentCaptor.forClass(MdnsPacket::class.java)
+ verify(repository).getConflictingServices(packetCaptor.capture())
+
+ packetCaptor.value.let {
+ assertEquals(0, it.questions.size)
+ assertEquals(1, it.answers.size)
+ assertEquals(0, it.authorityRecords.size)
+ assertEquals(0, it.additionalRecords.size)
+
+ assertTrue(it.answers[0] is MdnsTextRecord)
+ assertContentEquals(arrayOf("_testservice", "_tcp", "local"), it.answers[0].name)
+ }
+
+ thread.waitForIdle(TIMEOUT_MS)
+ verify(cb).onServiceConflict(advertiser, TEST_SERVICE_ID_1)
+ }
+
+ @Test
+ fun testRestartProbingForConflict() {
+ val mockProbingInfo = mock(ProbingInfo::class.java)
+ doReturn(mockProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
+
+ advertiser.restartProbingForConflict(TEST_SERVICE_ID_1)
+
+ verify(prober).restartForConflict(mockProbingInfo)
+ }
+
+ @Test
+ fun testRenameServiceForConflict() {
+ val mockProbingInfo = mock(ProbingInfo::class.java)
+ doReturn(mockProbingInfo).`when`(repository).renameServiceForConflict(
+ TEST_SERVICE_ID_1, TEST_SERVICE_1)
+
+ advertiser.renameServiceForConflict(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+
+ verify(prober).restartForConflict(mockProbingInfo)
+ }
+
private fun addServiceAndFinishProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
AnnouncementInfo {
val testProbingInfo = mock(ProbingInfo::class.java)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt
new file mode 100644
index 0000000..f88da1f
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketTest.kt
@@ -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.connectivity.mdns
+
+import android.net.InetAddresses
+import com.android.net.module.util.HexDump
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+class MdnsPacketTest {
+ @Test
+ fun testParseQuery() {
+ // Probe packet with 1 question for Android.local, and 4 additionalRecords with 4 addresses
+ // for Android.local (similar to legacy mdnsresponder probes, although it used to put 4
+ // identical questions(!!) for Android.local when there were 4 addresses).
+ val packetHex = "00000000000100000004000007416e64726f6964056c6f63616c0000ff0001c00c000100" +
+ "01000000780004c000027bc00c001c000100000078001020010db8000000000000000000000123c0" +
+ "0c001c000100000078001020010db8000000000000000000000456c00c001c000100000078001020" +
+ "010db8000000000000000000000789"
+
+ val bytes = HexDump.hexStringToByteArray(packetHex)
+ val reader = MdnsPacketReader(bytes, bytes.size)
+ val packet = MdnsPacket.parse(reader)
+
+ assertEquals(1, packet.questions.size)
+ assertEquals(0, packet.answers.size)
+ assertEquals(4, packet.authorityRecords.size)
+ assertEquals(0, packet.additionalRecords.size)
+
+ val hostname = arrayOf("Android", "local")
+ packet.questions[0].let {
+ assertTrue(it is MdnsAnyRecord)
+ assertContentEquals(hostname, it.name)
+ }
+
+ packet.authorityRecords.forEach {
+ assertTrue(it is MdnsInetAddressRecord)
+ assertContentEquals(hostname, it.name)
+ assertEquals(120000, it.ttl)
+ }
+
+ assertEquals(InetAddresses.parseNumericAddress("192.0.2.123"),
+ (packet.authorityRecords[0] as MdnsInetAddressRecord).inet4Address)
+ assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"),
+ (packet.authorityRecords[1] as MdnsInetAddressRecord).inet6Address)
+ assertEquals(InetAddresses.parseNumericAddress("2001:db8::456"),
+ (packet.authorityRecords[2] as MdnsInetAddressRecord).inet6Address)
+ assertEquals(InetAddresses.parseNumericAddress("2001:db8::789"),
+ (packet.authorityRecords[3] as MdnsInetAddressRecord).inet6Address)
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
index 3caa97d..a2dbbc6 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -114,7 +114,7 @@
@Test
fun testProbe() {
- val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
val prober = TestProber(thread.looper, replySender, cb)
val probeInfo = TestProbeInfo(
listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)))
@@ -129,7 +129,7 @@
@Test
fun testProbeMultipleRecords() {
- val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
val prober = TestProber(thread.looper, replySender, cb)
val probeInfo = TestProbeInfo(listOf(
makeServiceRecord(TEST_SERVICE_NAME_1, 37890),
@@ -167,7 +167,7 @@
@Test
fun testStopProbing() {
- val replySender = MdnsReplySender(thread.looper, socket, buffer)
+ val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
val prober = TestProber(thread.looper, replySender, cb)
val probeInfo = TestProbeInfo(
listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890)),
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
index 29d0854..ecc11ec 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -21,10 +21,13 @@
import android.net.nsd.NsdServiceInfo
import android.os.Build
import android.os.HandlerThread
+import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
import com.android.server.connectivity.mdns.MdnsRecordRepository.Dependencies
import com.android.server.connectivity.mdns.MdnsRecordRepository.getReverseDnsAddress
+import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import java.net.InetSocketAddress
import java.net.NetworkInterface
import java.util.Collections
import kotlin.test.assertContentEquals
@@ -150,11 +153,7 @@
@Test
fun testExitAnnouncements() {
val repository = MdnsRecordRepository(thread.looper, deps)
- repository.updateAddresses(TEST_ADDRESSES)
-
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
- repository.onProbingSucceeded(probingInfo)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
@@ -183,9 +182,7 @@
@Test
fun testExitingServiceReAdded() {
val repository = MdnsRecordRepository(thread.looper, deps)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
- repository.onProbingSucceeded(probingInfo)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.exitService(TEST_SERVICE_ID_1)
@@ -199,11 +196,8 @@
@Test
fun testOnProbingSucceeded() {
val repository = MdnsRecordRepository(thread.looper, deps)
- repository.updateAddresses(TEST_ADDRESSES)
-
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
- val announcementInfo = repository.onProbingSucceeded(probingInfo)
+ val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.onAdvertisementSent(TEST_SERVICE_ID_1)
val packet = announcementInfo.getPacket(0)
assertEquals(0x8400 /* response, authoritative */, packet.flags)
@@ -322,4 +316,155 @@
val expectedV4 = "123.2.0.192.in-addr.arpa".split(".").toTypedArray()
assertContentEquals(expectedV4, getReverseDnsAddress(parseNumericAddress("192.0.2.123")))
}
+
+ @Test
+ fun testGetReply() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ val questions = listOf(MdnsPointerRecord(arrayOf("_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ // TTL and data is empty for a question
+ 0L /* ttlMillis */,
+ null /* pointer */))
+ val query = MdnsPacket(0 /* flags */, questions, listOf() /* answers */,
+ listOf() /* authorityRecords */, listOf() /* additionalRecords */)
+ val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val reply = repository.getReply(query, src)
+
+ assertNotNull(reply)
+ // Source address is IPv4
+ assertEquals(MdnsConstants.getMdnsIPv4Address(), reply.destination.address)
+ assertEquals(MdnsConstants.MDNS_PORT, reply.destination.port)
+
+ // TTLs as per RFC6762 10.
+ val longTtl = 4_500_000L
+ val shortTtl = 120_000L
+ val serviceName = arrayOf("MyTestService", "_testservice", "_tcp", "local")
+
+ assertEquals(listOf(
+ MdnsPointerRecord(
+ arrayOf("_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ longTtl,
+ serviceName),
+ ), reply.answers)
+
+ assertEquals(listOf(
+ MdnsTextRecord(
+ serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ longTtl,
+ listOf() /* entries */),
+ MdnsServiceRecord(
+ serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ shortTtl,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ TEST_PORT,
+ TEST_HOSTNAME),
+ MdnsInetAddressRecord(
+ TEST_HOSTNAME,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ shortTtl,
+ TEST_ADDRESSES[0].address),
+ MdnsInetAddressRecord(
+ TEST_HOSTNAME,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ shortTtl,
+ TEST_ADDRESSES[1].address),
+ MdnsInetAddressRecord(
+ TEST_HOSTNAME,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ shortTtl,
+ TEST_ADDRESSES[2].address),
+ MdnsNsecRecord(
+ serviceName,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ longTtl,
+ serviceName /* nextDomain */,
+ intArrayOf(MdnsRecord.TYPE_TXT, MdnsRecord.TYPE_SRV)),
+ MdnsNsecRecord(
+ TEST_HOSTNAME,
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ shortTtl,
+ TEST_HOSTNAME /* nextDomain */,
+ intArrayOf(MdnsRecord.TYPE_A, MdnsRecord.TYPE_AAAA)),
+ ), reply.additionalAnswers)
+ }
+
+ @Test
+ fun testGetConflictingServices() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
+
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MyTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port + 1,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MyOtherTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ listOf(TextEntry.fromString("somedifferent=entry"))),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(setOf(TEST_SERVICE_ID_1, TEST_SERVICE_ID_2),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
+ fun testGetConflictingServices_IdenticalService() {
+ val repository = MdnsRecordRepository(thread.looper, deps)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MyTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MyOtherTestService", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, emptyList()),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ // Above records are identical to the actual registrations: no conflict
+ assertEquals(emptySet(), repository.getConflictingServices(packet))
+ }
+}
+
+private fun MdnsRecordRepository.initWithService(serviceId: Int, serviceInfo: NsdServiceInfo):
+ AnnouncementInfo {
+ updateAddresses(TEST_ADDRESSES)
+ addService(serviceId, serviceInfo)
+ val probingInfo = setServiceProbing(serviceId)
+ assertNotNull(probingInfo)
+ return onProbingSucceeded(probingInfo)
}
diff --git a/tools/gn2bp/Android.bp.swp b/tools/gn2bp/Android.bp.swp
index ebf1a9b..9f34b06 100644
--- a/tools/gn2bp/Android.bp.swp
+++ b/tools/gn2bp/Android.bp.swp
@@ -14,6 +14,13 @@
//
// This file is automatically generated by gen_android_bp. Do not edit.
+// GN: PACKAGE
+package {
+ default_applicable_licenses: [
+ "external_cronet_license",
+ ],
+}
+
// GN: //components/cronet/android:cronet_api_java
java_library {
name: "cronet_aml_api_java",
@@ -22,11 +29,13 @@
],
libs: [
"androidx.annotation_annotation",
+ "framework-annotations-lib",
],
sdk_version: "module_current",
}
// GN: //components/cronet/android:cronet_api_java
+// TODO(danstahr): add the API helpers separately after the main API is checked in and thoroughly reviewed
filegroup {
name: "cronet_aml_api_sources",
srcs: [
@@ -52,18 +61,6 @@
"components/cronet/android/api/src/android/net/http/UploadDataSink.java",
"components/cronet/android/api/src/android/net/http/UrlRequest.java",
"components/cronet/android/api/src/android/net/http/UrlResponseInfo.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/ByteArrayCallback.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/ContentTypeParametersParser.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/HttpResponse.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/ImplicitFlowControlCallback.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/InMemoryTransformCallback.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/JsonCallback.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/RedirectHandler.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/RedirectHandlers.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/RequestCompletionListener.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/StringCallback.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/UploadDataProviders.java",
- "components/cronet/android/api/src/android/net/http/apihelpers/UrlRequestCallbacks.java",
],
}
@@ -139,7 +136,7 @@
// GN: //base/allocator/partition_allocator:debugging_buildflags
cc_genrule {
name: "cronet_aml_base_allocator_partition_allocator_debugging_buildflags",
- cmd: "echo '--flags PA_DCHECK_IS_ON=\"true\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"true\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
+ cmd: "echo '--flags PA_DCHECK_IS_ON=\"false\" PA_EXPENSIVE_DCHECKS_ARE_ON=\"false\" PA_DCHECK_IS_CONFIGURABLE=\"false\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -262,12 +259,14 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
"-DIS_PARTITION_ALLOC_IMPL",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DPA_PCSCAN_STACK_SUPPORTED",
- "-D_DEBUG",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -384,7 +383,7 @@
"--input_file " +
"java/lang/Runtime.class " +
"--javap " +
- "$$(find out/.path -name javap) " +
+ "$$(find $${OUT_DIR:-out}/.path -name javap) " +
"--package_prefix " +
"android.net.http.internal",
out: [
@@ -408,6 +407,7 @@
cc_library_static {
name: "cronet_aml_base_base",
srcs: [
+ ":cronet_aml_base_nodebug_assertion",
":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",
@@ -968,17 +968,19 @@
"-DBASE_IMPLEMENTATION",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
"-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-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_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -1361,10 +1363,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -1396,7 +1400,7 @@
cc_genrule {
name: "cronet_aml_base_build_date",
cmd: "$(location build/write_build_date_header.py) $(out) " +
- "1672549200",
+ "1674804594",
out: [
"base/generated_build_date.h",
],
@@ -1459,7 +1463,7 @@
name: "cronet_aml_base_debugging_buildflags",
cmd: "if [[ ( $$CC_ARCH == 'x86_64' && $$CC_OS == 'android' ) ]]; " +
"then " +
- "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"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\"' | " +
+ "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"false\" UNSAFE_DEVELOPER_BUILD=\"false\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"false\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"false\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -1471,7 +1475,7 @@
"fi; " +
"if [[ ( $$CC_ARCH == 'x86' && $$CC_OS == 'android' ) ]]; " +
"then " +
- "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"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\"' | " +
+ "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"true\" UNSAFE_DEVELOPER_BUILD=\"false\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"false\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"false\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -1483,7 +1487,7 @@
"fi; " +
"if [[ ( $$CC_ARCH == 'arm' && $$CC_OS == 'android' ) ]]; " +
"then " +
- "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"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\"' | " +
+ "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"false\" UNSAFE_DEVELOPER_BUILD=\"false\" CAN_UNWIND_WITH_CFI_TABLE=\"true\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"false\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"false\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -1495,7 +1499,7 @@
"fi; " +
"if [[ ( $$CC_ARCH == 'arm64' && $$CC_OS == 'android' ) ]]; " +
"then " +
- "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"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\"' | " +
+ "echo '--flags DCHECK_IS_CONFIGURABLE=\"false\" ENABLE_LOCATION_SOURCE=\"true\" FROM_HERE_USES_LOCATION_BUILTINS=\"true\" ENABLE_PROFILING=\"false\" CAN_UNWIND_WITH_FRAME_POINTERS=\"true\" UNSAFE_DEVELOPER_BUILD=\"false\" CAN_UNWIND_WITH_CFI_TABLE=\"false\" EXCLUDE_UNWIND_TABLES=\"false\" ENABLE_GDBINIT_WARNING=\"false\" ENABLE_LLDBINIT_WARNING=\"false\" EXPENSIVE_DCHECKS_ARE_ON=\"false\" ENABLE_STACK_TRACE_LINE_NUMBERS=\"false\"' | " +
"$(location build/write_buildflag_header.py) --output " +
"$(out) " +
"--rulename " +
@@ -1660,6 +1664,57 @@
],
}
+// GN: //base:nodebug_assertion
+cc_object {
+ name: "cronet_aml_base_nodebug_assertion",
+ srcs: [
+ "base/nodebug_assertion.cc",
+ ],
+ static_libs: [
+ "cronet_aml_base_base_static",
+ ],
+ defaults: [
+ "cronet_aml_defaults",
+ ],
+ cflags: [
+ "-DANDROID",
+ "-DANDROID_NDK_VERSION_ROLL=r23_1",
+ "-DBASE_IMPLEMENTATION",
+ "-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
+ "-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
+ "-DHAVE_SYS_UIO_H",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
+ "-D_GNU_SOURCE",
+ "-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
+ "-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++17",
+ target: {
+ android_x86: {
+ cflags: [
+ "-msse3",
+ ],
+ },
+ android_x86_64: {
+ cflags: [
+ "-msse3",
+ ],
+ },
+ },
+}
+
// GN: //base:orderfile_buildflags
cc_genrule {
name: "cronet_aml_base_orderfile_buildflags",
@@ -1858,10 +1913,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -1903,10 +1960,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -1965,7 +2023,7 @@
"soong_zip",
],
cmd: "cp $(in) $(genDir)/BuildConfig.java && " +
- "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/BuildConfig.java",
+ "$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/BuildConfig.java",
out: [
"BuildConfig.srcjar",
],
@@ -1979,7 +2037,6 @@
],
cflags: [
"-DANDROID",
- "-D_ENABLE_ASSERTS",
"-E",
"-P",
],
@@ -2162,11 +2219,13 @@
cflags: [
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DLIBCXX_BUILDING_LIBCXXABI",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
+ "-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1",
"-D_LIBCPP_BUILDING_LIBRARY",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__((__visibility__(\"default\")))",
@@ -2218,6 +2277,7 @@
host: {
cflags: [
"-DCR_SYSROOT_KEY=20220331T153654Z-0",
+ "-DNO_UNWIND_TABLES",
"-DUSE_AURA=1",
"-DUSE_OZONE=1",
"-DUSE_UDEV",
@@ -2258,10 +2318,11 @@
cflags: [
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DLIBCXXABI_SILENT_TERMINATE",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_BUILDING_LIBRARY",
"-D_LIBCPP_CONSTINIT=constinit",
@@ -2329,6 +2390,7 @@
],
cflags: [
"-DCR_SYSROOT_KEY=20220331T153654Z-0",
+ "-DNO_UNWIND_TABLES",
"-DUSE_AURA=1",
"-DUSE_OZONE=1",
"-DUSE_UDEV",
@@ -2435,14 +2497,16 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -2462,8 +2526,8 @@
"third_party/protobuf/src/",
],
cpp_std: "c++17",
- linker_scripts: [
- "base/android/library_loader/anchor_functions.lds",
+ ldflags: [
+ "-Wl,--script,external/cronet/base/android/library_loader/anchor_functions.lds",
],
stem: "libcronet.108.0.5359.128",
target: {
@@ -3099,14 +3163,16 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3162,7 +3228,8 @@
// 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 " +
+ cmd: "$(location build/util/version.py) --official " +
+ "-f " +
"$(location chrome/VERSION) " +
"-f " +
"$(location build/util/LASTCHANGE) " +
@@ -3193,7 +3260,7 @@
"soong_zip",
],
cmd: "cp $(in) $(genDir)/IntegratedModeState.java && " +
- "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/IntegratedModeState.java",
+ "$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/IntegratedModeState.java",
out: [
"IntegratedModeState.srcjar",
],
@@ -3228,7 +3295,8 @@
// 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 " +
+ cmd: "$(location build/util/version.py) --official " +
+ "-f " +
"$(location chrome/VERSION) " +
"-f " +
"$(location build/util/LASTCHANGE) " +
@@ -3259,7 +3327,7 @@
"soong_zip",
],
cmd: "cp $(in) $(genDir)/LoadState.java && " +
- "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/LoadState.java",
+ "$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/LoadState.java",
out: [
"LoadState.srcjar",
],
@@ -3461,14 +3529,16 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3505,7 +3575,8 @@
// GN: //components/cronet:cronet_version_header_action
cc_genrule {
name: "cronet_aml_components_cronet_cronet_version_header_action",
- cmd: "$(location build/util/version.py) -f " +
+ cmd: "$(location build/util/version.py) --official " +
+ "-f " +
"$(location chrome/VERSION) " +
"-e " +
"'VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)' " +
@@ -3557,10 +3628,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3627,14 +3700,16 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3762,10 +3837,12 @@
"-DCOMPONENTS_PREFS_IMPLEMENTATION",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3873,10 +3950,12 @@
"-DCRYPTO_IMPLEMENTATION",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -3913,7 +3992,6 @@
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-O2",
"-Wno-ambiguous-reversed-operator",
- "-Wno-deprecated-non-prototype",
"-Wno-error=return-type",
"-Wno-macro-redefined",
"-Wno-missing-field-initializers",
@@ -4282,7 +4360,7 @@
"soong_zip",
],
cmd: "cp $(in) $(genDir)/NetError.java && " +
- "$(location soong_zip) -o $(out) -srcjar -f $(genDir)/NetError.java",
+ "$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/NetError.java",
out: [
"NetError.srcjar",
],
@@ -4517,16 +4595,18 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
"-DNET_IMPLEMENTATION",
- "-D_DEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -4619,16 +4699,18 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
"-DNET_IMPLEMENTATION",
- "-D_DEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -4734,16 +4816,18 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
"-DNET_IMPLEMENTATION",
- "-D_DEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -5400,16 +5484,18 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
"-DNET_IMPLEMENTATION",
- "-D_DEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -5505,16 +5591,18 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
"-DNET_IMPLEMENTATION",
- "-D_DEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -5737,14 +5825,16 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -5808,10 +5898,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6301,15 +6393,17 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-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",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6376,10 +6470,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6439,11 +6535,13 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
"-DIS_URI_TEMPLATE_IMPL",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6492,10 +6590,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6537,10 +6636,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6582,10 +6682,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6627,10 +6728,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6672,10 +6774,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6717,10 +6820,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6762,10 +6866,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6808,10 +6913,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6853,10 +6959,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6900,10 +7007,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6945,10 +7053,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -6990,10 +7099,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7035,10 +7145,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7080,10 +7191,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7125,10 +7237,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7170,10 +7283,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7215,10 +7329,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7260,10 +7375,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7305,10 +7421,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7350,10 +7467,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7396,10 +7514,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7444,10 +7563,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7492,10 +7612,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7540,10 +7661,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7588,10 +7710,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7636,10 +7759,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7684,10 +7808,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7729,10 +7854,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7774,10 +7900,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7822,10 +7949,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7868,10 +7996,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7913,10 +8042,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -7960,10 +8090,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8011,10 +8142,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8056,10 +8188,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8101,10 +8234,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8146,10 +8280,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8193,10 +8328,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8243,10 +8379,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8300,10 +8437,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8345,10 +8483,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8396,10 +8535,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8441,10 +8581,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8494,10 +8635,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8543,10 +8685,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8588,10 +8731,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8633,10 +8777,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8677,10 +8822,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -8721,10 +8867,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9035,11 +9183,12 @@
"-DBORINGSSL_NO_STATIC_INITIALIZER",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DOPENSSL_SMALL",
- "-D_DEBUG",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9077,10 +9226,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9199,10 +9350,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9251,10 +9404,11 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9537,11 +9691,13 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_DLOPEN=0",
"-DHAVE_SYS_UIO_H",
"-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DUCONFIG_ONLY_HTML_CONVERSION=1",
"-DUCONFIG_USE_WINDOWS_LCID_MAPPING_API=0",
"-DUSE_CHROMIUM_ICU=1",
@@ -9552,7 +9708,6 @@
"-DU_I18N_IMPLEMENTATION",
"-DU_STATIC_IMPLEMENTATION",
"-DU_USING_ICU_NAMESPACE=0",
- "-D_DEBUG",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9793,11 +9948,13 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_DLOPEN=0",
"-DHAVE_SYS_UIO_H",
"-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DUCONFIG_ONLY_HTML_CONVERSION=1",
"-DUCONFIG_USE_WINDOWS_LCID_MAPPING_API=0",
"-DUSE_CHROMIUM_ICU=1",
@@ -9809,7 +9966,6 @@
"-DU_ICUDATAENTRY_IN_COMMON",
"-DU_STATIC_IMPLEMENTATION",
"-DU_USING_ICU_NAMESPACE=0",
- "-D_DEBUG",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -9865,11 +10021,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_CONFIG_H",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -10055,10 +10212,12 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -10187,17 +10346,19 @@
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
"-DCR_SYSROOT_KEY=20220331T153654Z-0",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_ZLIB",
+ "-DNDEBUG",
+ "-DNO_UNWIND_TABLES",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DUSE_AURA=1",
"-DUSE_OZONE=1",
"-DUSE_UDEV",
- "-D_DEBUG",
"-D_FILE_OFFSET_BITS=64",
"-D_GNU_SOURCE",
"-D_LARGEFILE64_SOURCE",
@@ -10264,14 +10425,15 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
"-DHAVE_SYS_UIO_H",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -10322,16 +10484,18 @@
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
"-DCR_SYSROOT_KEY=20220331T153654Z-0",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
+ "-DNDEBUG",
+ "-DNO_UNWIND_TABLES",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DUSE_AURA=1",
"-DUSE_OZONE=1",
"-DUSE_UDEV",
- "-D_DEBUG",
"-D_FILE_OFFSET_BITS=64",
"-D_GNU_SOURCE",
"-D_LARGEFILE64_SOURCE",
@@ -10455,16 +10619,18 @@
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
"-DCR_SYSROOT_KEY=20220331T153654Z-0",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DGOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0",
"-DGOOGLE_PROTOBUF_NO_RTTI",
"-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
"-DHAVE_PTHREAD",
+ "-DNDEBUG",
+ "-DNO_UNWIND_TABLES",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
"-DUSE_AURA=1",
"-DUSE_OZONE=1",
"-DUSE_UDEV",
- "-D_DEBUG",
"-D_FILE_OFFSET_BITS=64",
"-D_GNU_SOURCE",
"-D_LARGEFILE64_SOURCE",
@@ -10571,11 +10737,13 @@
"-DANDROID_NDK_VERSION_ROLL=r23_1",
"-DCR_CLANG_REVISION=\"llvmorg-16-init-6578-g0d30e92f-2\"",
"-DCR_LIBCXX_REVISION=64d36e572d3f9719c5d75011a718f33f11126851",
- "-DDCHECK_ALWAYS_ON=1",
- "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
+ "-DDYNAMIC_ANNOTATIONS_ENABLED=0",
"-DHAVE_SYS_UIO_H",
"-DIS_URL_IMPL",
- "-D_DEBUG",
+ "-DNDEBUG",
+ "-DNVALGRIND",
+ "-DOFFICIAL_BUILD",
+ "-D_FORTIFY_SOURCE=2",
"-D_GNU_SOURCE",
"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -10645,3 +10813,55 @@
],
}
+// GN: LICENSE
+license {
+ name: "external_cronet_license",
+ license_kinds: [
+ "SPDX-license-identifier-AFL-2.0",
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-BSL-1.0",
+ "SPDX-license-identifier-GPL",
+ "SPDX-license-identifier-GPL-2.0",
+ "SPDX-license-identifier-GPL-3.0",
+ "SPDX-license-identifier-ICU",
+ "SPDX-license-identifier-ISC",
+ "SPDX-license-identifier-LGPL",
+ "SPDX-license-identifier-LGPL-2.1",
+ "SPDX-license-identifier-MIT",
+ "SPDX-license-identifier-MPL",
+ "SPDX-license-identifier-MPL-2.0",
+ "SPDX-license-identifier-NCSA",
+ "SPDX-license-identifier-OpenSSL",
+ "SPDX-license-identifier-Unicode-DFS",
+ "legacy_unencumbered",
+ ],
+ license_text: [
+ "LICENSE",
+ "base/third_party/double_conversion/LICENSE",
+ "base/third_party/dynamic_annotations/LICENSE",
+ "base/third_party/icu/LICENSE",
+ "base/third_party/nspr/LICENSE",
+ "base/third_party/superfasthash/LICENSE",
+ "base/third_party/symbolize/LICENSE",
+ "base/third_party/valgrind/LICENSE",
+ "base/third_party/xdg_user_dirs/LICENSE",
+ "net/third_party/quiche/src/LICENSE",
+ "net/third_party/uri_template/LICENSE",
+ "third_party/abseil-cpp/LICENSE",
+ "third_party/ashmem/LICENSE",
+ "third_party/boringssl/src/LICENSE",
+ "third_party/boringssl/src/third_party/fiat/LICENSE",
+ "third_party/boringssl/src/third_party/googletest/LICENSE",
+ "third_party/boringssl/src/third_party/wycheproof_testvectors/LICENSE",
+ "third_party/brotli/LICENSE",
+ "third_party/icu/LICENSE",
+ "third_party/icu/scripts/LICENSE",
+ "third_party/libevent/LICENSE",
+ "third_party/metrics_proto/LICENSE",
+ "third_party/modp_b64/LICENSE",
+ "third_party/protobuf/LICENSE",
+ "third_party/protobuf/third_party/utf8_range/LICENSE",
+ ],
+}
+
diff --git a/tools/gn2bp/desc_arm.json b/tools/gn2bp/desc_arm.json
index aa76d1c..ff1a7e2 100644
--- a/tools/gn2bp/desc_arm.json
+++ b/tools/gn2bp/desc_arm.json
Binary files differ
diff --git a/tools/gn2bp/desc_arm64.json b/tools/gn2bp/desc_arm64.json
index 2bdbc03..20c942f 100644
--- a/tools/gn2bp/desc_arm64.json
+++ b/tools/gn2bp/desc_arm64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x64.json b/tools/gn2bp/desc_x64.json
index ddf9d3e..b25932b 100644
--- a/tools/gn2bp/desc_x64.json
+++ b/tools/gn2bp/desc_x64.json
Binary files differ
diff --git a/tools/gn2bp/desc_x86.json b/tools/gn2bp/desc_x86.json
index f57f15a..b4bc6e9 100644
--- a/tools/gn2bp/desc_x86.json
+++ b/tools/gn2bp/desc_x86.json
Binary files differ
diff --git a/tools/gn2bp/gen_android_bp b/tools/gn2bp/gen_android_bp
index e10c415..9d2d858 100755
--- a/tools/gn2bp/gen_android_bp
+++ b/tools/gn2bp/gen_android_bp
@@ -128,12 +128,17 @@
"-msse4.2",
]
+def get_linker_script_ldflag(script_path):
+ return f'-Wl,--script,{tree_path}/{script_path}'
+
# 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',
+ # linker_scripts property is not available in tm-mainline-prod.
+ # So use ldflags to specify linker script.
+ ('ldflags',{
+ get_linker_script_ldflag('base/android/library_loader/anchor_functions.lds'),
}),
],
'cronet_aml_net_net': [
@@ -370,6 +375,7 @@
self.min_sdk_version = None
self.proto = dict()
self.linker_scripts = set()
+ self.ldflags = 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()
@@ -391,6 +397,9 @@
self.processor_class = None
self.sdk_version = None
self.javacflags = set()
+ self.license_kinds = set()
+ self.license_text = set()
+ self.default_applicable_licenses = set()
def to_string(self, output):
if self.comment:
@@ -437,6 +446,7 @@
self._output_field(output, 'stubs')
self._output_field(output, 'proto')
self._output_field(output, 'linker_scripts')
+ self._output_field(output, 'ldflags')
self._output_field(output, 'cppflags')
self._output_field(output, 'libs')
self._output_field(output, 'stem')
@@ -446,6 +456,9 @@
self._output_field(output, 'processor_class')
self._output_field(output, 'sdk_version')
self._output_field(output, 'javacflags')
+ self._output_field(output, 'license_kinds')
+ self._output_field(output, 'license_text')
+ self._output_field(output, 'default_applicable_licenses')
if self.rtti:
self._output_field(output, 'rtti')
@@ -749,7 +762,7 @@
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'
+ f'$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/{stem}.java'
])
module.tools.add('soong_zip')
blueprint.add_module(module)
@@ -956,7 +969,7 @@
def _sanitize_args(self):
self._set_value_arg('--jar_file', '$(location :current_android_jar)', False)
if self._has_arg('--jar_file'):
- self._append_arg('--javap', '$$(find out/.path -name javap)')
+ self._append_arg('--javap', '$$(find $${OUT_DIR:-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)
@@ -1551,8 +1564,12 @@
def create_java_api_module(blueprint, gn):
source_module = Module('filegroup', module_prefix + 'api_sources', java_api_target_name)
+ # TODO add the API helpers separately after the main API is checked in and thoroughly reviewed
source_module.srcs.update([gn_utils.label_to_path(source)
- for source in get_api_java_sources(gn)])
+ for source in get_api_java_sources(gn)
+ if "apihelpers" not in source])
+ source_module.comment += "\n// TODO(danstahr): add the API helpers separately after the main" \
+ " API is checked in and thoroughly reviewed"
source_module.srcs.update([
':' + create_action_module(blueprint, gn.get_target(dep), 'java_genrule').name
for dep in get_api_java_actions(gn)])
@@ -1563,6 +1580,7 @@
java_module.sdk_version = "module_current"
java_module.libs = {
"androidx.annotation_annotation",
+ "framework-annotations-lib",
}
blueprint.add_module(java_module)
return java_module
@@ -1589,7 +1607,6 @@
'-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
@@ -1647,6 +1664,59 @@
return blueprint
+def create_license_module(blueprint):
+ module = Module("license", "external_cronet_license", "LICENSE")
+ module.license_kinds.update({
+ 'SPDX-license-identifier-LGPL-2.1',
+ 'SPDX-license-identifier-GPL-2.0',
+ 'SPDX-license-identifier-MPL',
+ 'SPDX-license-identifier-ISC',
+ 'SPDX-license-identifier-GPL',
+ 'SPDX-license-identifier-AFL-2.0',
+ 'SPDX-license-identifier-MPL-2.0',
+ 'SPDX-license-identifier-BSD',
+ 'SPDX-license-identifier-Apache-2.0',
+ 'SPDX-license-identifier-BSL-1.0',
+ 'SPDX-license-identifier-LGPL',
+ 'SPDX-license-identifier-GPL-3.0',
+ 'SPDX-license-identifier-Unicode-DFS',
+ 'SPDX-license-identifier-NCSA',
+ 'SPDX-license-identifier-OpenSSL',
+ 'SPDX-license-identifier-MIT',
+ "SPDX-license-identifier-ICU",
+ 'legacy_unencumbered', # public domain
+ })
+ module.license_text.update({
+ "LICENSE",
+ "net/third_party/uri_template/LICENSE",
+ "net/third_party/quiche/src/LICENSE",
+ "base/third_party/symbolize/LICENSE",
+ "base/third_party/superfasthash/LICENSE",
+ "base/third_party/xdg_user_dirs/LICENSE",
+ "base/third_party/double_conversion/LICENSE",
+ "base/third_party/nspr/LICENSE",
+ "base/third_party/dynamic_annotations/LICENSE",
+ "base/third_party/icu/LICENSE",
+ "base/third_party/valgrind/LICENSE",
+ "third_party/brotli/LICENSE",
+ "third_party/protobuf/LICENSE",
+ "third_party/protobuf/third_party/utf8_range/LICENSE",
+ "third_party/metrics_proto/LICENSE",
+ "third_party/boringssl/src/LICENSE",
+ "third_party/boringssl/src/third_party/googletest/LICENSE",
+ "third_party/boringssl/src/third_party/wycheproof_testvectors/LICENSE",
+ "third_party/boringssl/src/third_party/fiat/LICENSE",
+ "third_party/libevent/LICENSE",
+ "third_party/ashmem/LICENSE",
+ "third_party/icu/LICENSE",
+ "third_party/icu/scripts/LICENSE",
+ "third_party/abseil-cpp/LICENSE",
+ "third_party/modp_b64/LICENSE",
+ })
+ default_license = Module("package", "", "PACKAGE")
+ default_license.default_applicable_licenses.add(module.name)
+ blueprint.add_module(module)
+ blueprint.add_module(default_license)
def main():
parser = argparse.ArgumentParser(
@@ -1698,7 +1768,7 @@
# 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)
-
+ create_license_module(blueprint)
output = [
"""// Copyright (C) 2022 The Android Open Source Project
//
diff --git a/tools/gn2bp/gen_desc_json.sh b/tools/gn2bp/gen_desc_json.sh
index ed684b3..1f60eb9 100755
--- a/tools/gn2bp/gen_desc_json.sh
+++ b/tools/gn2bp/gen_desc_json.sh
@@ -2,11 +2,34 @@
set -x
# Run this script inside a full chromium checkout.
-# TODO: add support for applying local patches.
OUT_PATH="out/cronet"
#######################################
+# Apply patches in external/cronet.
+# Globals:
+# ANDROID_BUILD_TOP
+# Arguments:
+# None
+#######################################
+function apply_patches() {
+ local -r patch_root="${ANDROID_BUILD_TOP}/external/cronet/patches"
+
+ local upstream_patches
+ upstream_patches=$(ls "${patch_root}/upstream-next")
+ local patch
+ for patch in ${upstream_patches}; do
+ git am --3way "${patch_root}/upstream-next/${patch}"
+ done
+
+ local local_patches
+ local_patches=$(ls "${patch_root}/local")
+ for patch in ${local_patches}; do
+ git am --3way "${patch_root}/local/${patch}"
+ done
+}
+
+#######################################
# Generate desc.json for a specified architecture.
# Globals:
# OUT_PATH
@@ -31,6 +54,8 @@
"treat_warnings_as_errors = false"
"enable_base_tracing = false"
"is_cronet_build = true"
+ "is_debug = false"
+ "is_official_build = true"
)
gn_args+=("target_cpu = \"${1}\"")
@@ -48,6 +73,7 @@
gn desc "${OUT_PATH}" --format=json --all-toolchains "//*" > "${out_file}"
}
+apply_patches
gn_desc x86
gn_desc x64
gn_desc arm