Merge "Reorder clatStart() to setup 464xlat sockets before tun interface" into main
diff --git a/Cronet/tests/OWNERS b/Cronet/tests/OWNERS
deleted file mode 100644
index a35a789..0000000
--- a/Cronet/tests/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Bug component: 31808
-
-set noparent
-file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking_xts
-
-# TODO: Temp ownership to develop cronet CTS
-colibie@google.com #{LAST_RESORT_SUGGESTION}
-prohr@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
deleted file mode 100644
index 0b6ecef..0000000
--- a/Cronet/tests/common/Android.bp
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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.
-//
-
-// Tests in this folder are included both in unit tests and CTS.
-// They must be fast and stable, and exercise public or test APIs.
-
-package {
- default_team: "trendy_team_fwk_core_networking",
- // See: http://go/android-license-faq
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-// TODO: Consider merging with ConnectivityCoverageTests which is a collection of all
-// Connectivity tests being used for coverage. This will depend on how far we decide to
-// go with merging NetHttp and Tethering targets.
-// Target used for coverage. Combines CTS tests and our MTS tests.
-android_test {
- name: "NetHttpCoverageTests",
- enforce_default_target_sdk_version: true,
- min_sdk_version: "30",
- test_suites: [
- "general-tests",
- "mts-tethering",
- ],
- static_libs: [
- "modules-utils-native-coverage-listener",
- "CtsNetHttpTestsLib",
- "NetHttpTestsLibPreJarJar",
- ],
- jarjar_rules: ":net-http-test-jarjar-rules",
- compile_multilib: "both", // Include both the 32 and 64 bit versions
- jni_libs: [
- "cronet_aml_components_cronet_android_cronet_tests__testing",
- "cronet_aml_third_party_netty_tcnative_netty_tcnative_so__testing",
- "libnativecoverage",
- ],
- data: [":cronet_javatests_resources"],
-}
-
-// MTS-only specific targets.
-java_genrule {
- name: "net-http-test-jarjar-rules",
- tool_files: [
- ":NetHttpTestsLibPreJarJar{.jar}",
- "jarjar_excludes.txt",
- ],
- tools: [
- "jarjar-rules-generator",
- ],
- out: ["net_http_test_jarjar_rules.txt"],
- cmd: "$(location jarjar-rules-generator) " +
- "$(location :NetHttpTestsLibPreJarJar{.jar}) " +
- "--prefix android.net.connectivity " +
- "--excludes $(location jarjar_excludes.txt) " +
- "--output $(out)",
-}
-
-android_library {
- name: "NetHttpTestsLibPreJarJar",
- static_libs: [
- "cronet_aml_api_java",
- "cronet_aml_java__testing",
- "cronet_java_tests",
- ],
- sdk_version: "module_current",
- min_sdk_version: "30",
-}
diff --git a/Cronet/tests/common/AndroidManifest.xml b/Cronet/tests/common/AndroidManifest.xml
deleted file mode 100644
index 418af86..0000000
--- a/Cronet/tests/common/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.net.http.tests.coverage">
-
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.INTERNET"/>
-
- <application android:networkSecurityConfig="@xml/network_security_config"
- tools:replace="android:label"
- android:label="NetHttp coverage tests" >
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.net.http.tests.coverage"
- android:label="NetHttp coverage tests">
- </instrumentation>
-</manifest>
diff --git a/Cronet/tests/common/AndroidTest.xml b/Cronet/tests/common/AndroidTest.xml
deleted file mode 100644
index bb7ed11..0000000
--- a/Cronet/tests/common/AndroidTest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
- ~ 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.
- -->
-<configuration description="Runs coverage tests for NetHttp">
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="test-file-name" value="NetHttpCoverageTests.apk" />
- <option name="install-arg" value="-t" />
- </target_preparer>
- <option name="test-tag" value="NetHttpCoverageTests" />
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="push-file" key="net" value="/storage/emulated/0/chromium_tests_root/net" />
- <option name="push-file" key="test_server" value="/storage/emulated/0/chromium_tests_root/components/cronet/testing/test_server" />
- </target_preparer>
- <!-- Tethering/Connectivity is a SDK 30+ module however Cronet is installed on 31+ due to b/270049141. -->
- <object type="module_controller"
- class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
- <!-- Only run NetHttpCoverageTests 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" />
- </object>
- <option name="config-descriptor:metadata" key="mainline-param"
- value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.net.http.tests.coverage" />
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <!-- b/298380508 -->
- <option name="exclude-filter" value="org.chromium.net.CronetUrlRequestContextTest#testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider" />
- <!-- b/316559294 -->
- <option name="exclude-filter" value="org.chromium.net.NQETest#testQuicDisabled" />
- <!-- b/316559294 -->
- <option name="exclude-filter" value="org.chromium.net.NQETest#testPrefsWriteRead" />
- <!-- b/316554711-->
- <option name="exclude-filter" value="org.chromium.net.NetworkChangesTest" />
- <!-- b/316550794 -->
- <option name="exclude-filter" value="org.chromium.net.impl.CronetLoggerTest#testEngineCreation" />
- <!-- b/327182569 -->
- <option name="exclude-filter" value="org.chromium.net.urlconnection.CronetURLStreamHandlerFactoryTest#testSetUrlStreamFactoryUsesCronetForNative" />
- <option name="hidden-api-checks" value="false"/>
- <option name="isolated-storage" value="false"/>
- <option name="orchestrator" value="true"/>
- <option
- name="device-listeners"
- value="com.android.modules.utils.testing.NativeCoverageHackInstrumentationListener" />
- </test>
-</configuration>
diff --git a/Cronet/tests/common/jarjar_excludes.txt b/Cronet/tests/common/jarjar_excludes.txt
deleted file mode 100644
index b5cdf6e..0000000
--- a/Cronet/tests/common/jarjar_excludes.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-# Exclude some test prefixes, as they can't be found after being jarjared.
-com\.android\.testutils\..+
-# jarjar-gen can't handle some kotlin object expression, exclude packages that include them
-androidx\..+
-# don't jarjar netty as it does JNI
-io\.netty\..+
-kotlin\.test\..+
-kotlin\.reflect\..+
-org\.mockito\..+
-# Do not jarjar the api classes
-android\.net\..+
-# cronet_tests.so is not jarjared and uses base classes. We can remove this when there's a
-# separate java base target to depend on.
-org\.chromium\.base\..+
-J\.cronet_tests_N(\$.+)?
-
-# don't jarjar automatically generated FooJni files.
-org\.chromium\.net\..+Jni(\$.+)?
-
-# Do not jarjar the tests and its utils as they also do JNI with cronet_tests.so
-org\.chromium\.net\..*Test.*(\$.+)?
-org\.chromium\.net\.NativeTestServer(\$.+)?
-org\.chromium\.net\.MockUrlRequestJobFactory(\$.+)?
-org\.chromium\.net\.QuicTestServer(\$.+)?
-org\.chromium\.net\.MockCertVerifier(\$.+)?
-org\.chromium\.net\.LogcatCapture(\$.+)?
-org\.chromium\.net\.ReportingCollector(\$.+)?
-org\.chromium\.net\.Http2TestServer(\$.+)?
-org\.chromium\.net\.Http2TestHandler(\$.+)?
\ No newline at end of file
diff --git a/Cronet/tests/common/res/raw/quicroot.pem b/Cronet/tests/common/res/raw/quicroot.pem
deleted file mode 100644
index af21b3e..0000000
--- a/Cronet/tests/common/res/raw/quicroot.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC/jCCAeagAwIBAgIUXOi6XoxnMUjJg4jeOwRhsdqEqEQwDQYJKoZIhvcNAQEL
-BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTIzMDYwMTExMjcwMFoXDTMz
-MDUyOTExMjcwMFowFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl9xCMPMIvfmJWz25AG/VtgWbqNs67HXQbXWf
-pDF2wjQpHVOYbfl7Zgly5O+5es1aUbJaGyZ9G6xuYSXKFnnYLoP7M86O05fQQBAj
-K+IE5nO6136ksCAfxCFTFfn4vhPvK8Vba5rqox4WeIXYKvHYSoiHz0ELrnFOHcyN
-Innyze7bLtkMCA1ShHpmvDCR+U3Uj6JwOfoirn29jjU/48/ORha7dcJYtYXk2eGo
-RJfrtIx20tXAaKaGnXOCGYbEVXTeQkQPqKFVzqP7+KYS/Y8eNFV35ugpLNES+44T
-bQ2QruTZdrNRjJkEoyiB/E53a0OUltB/R7Z0L0xstnKfsAf3OwIDAQABo0IwQDAP
-BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUVdXNh2lk
-51/6hMmz0Z+OpIe8+f0wDQYJKoZIhvcNAQELBQADggEBADNg7G8n6DUrQ5doXzm9
-kOp5siX6iPs0zFReXKhIT1Gef63l3tb7AdPedF03aj9XkUt0shhNOGG5SK2k5KBQ
-MJc9muYRCAyo2xMr3rFUQdI5B51SCy5HeAMralgTHXN0Hv+TH04YfRrACVmr+5ke
-pH3bF1gYaT+Zy5/pHJnV5lcwS6/H44g9XXWIopjWCwbfzKxIuWofqL4fiToPSIYu
-MCUI4bKZipcJT5O6rdz/S9lbgYVjOJ4HAoT2icNQqNMMfULKevmF8SdJzfNd35yn
-tAKTROhIE2aQRVCclrjo/T3eyjWGGoJlGmxKbeCf/rXzcn1BRtk/UzLnbUFFlg5l
-axw=
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/Cronet/tests/common/res/values/cronet-test-rule-configuration.xml b/Cronet/tests/common/res/values/cronet-test-rule-configuration.xml
deleted file mode 100644
index 48ce420..0000000
--- a/Cronet/tests/common/res/values/cronet-test-rule-configuration.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<resources>
- <bool name="is_running_in_aosp">true</bool>
-</resources>
\ No newline at end of file
diff --git a/Cronet/tests/common/res/xml/network_security_config.xml b/Cronet/tests/common/res/xml/network_security_config.xml
deleted file mode 100644
index 32b7171..0000000
--- a/Cronet/tests/common/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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.
- -->
-
-<network-security-config>
- <base-config>
- <trust-anchors>
- <certificates src="@raw/quicroot"/>
- <certificates src="system"/>
- </trust-anchors>
- </base-config>
- <!-- Since Android 9 (API 28) cleartext support is disabled by default, this
- causes some of our tests to fail (see crbug/1220357).
- The following configs allow http requests for the domains used in these
- tests.
-
- TODO(stefanoduo): Figure out if we really need to use http for these tests
- -->
- <domain-config cleartextTrafficPermitted="true">
- <!-- Used as the base URL by native test server (net::EmbeddedTestServer) -->
- <domain includeSubdomains="true">127.0.0.1</domain>
- <!-- Used by CronetHttpURLConnectionTest#testIOExceptionInterruptRethrown -->
- <domain includeSubdomains="true">localhost</domain>
- <!-- Used by CronetHttpURLConnectionTest#testBadIP -->
- <domain includeSubdomains="true">0.0.0.0</domain>
- <!-- Used by CronetHttpURLConnectionTest#testSetUseCachesFalse -->
- <domain includeSubdomains="true">host-cache-test-host</domain>
- <!-- Used by CronetHttpURLConnectionTest#testBadHostname -->
- <domain includeSubdomains="true">this-weird-host-name-does-not-exist</domain>
- <!-- Used by CronetUrlRequestContextTest#testHostResolverRules -->
- <domain includeSubdomains="true">some-weird-hostname</domain>
- </domain-config>
-</network-security-config>
\ No newline at end of file
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
deleted file mode 100644
index 92b73d9..0000000
--- a/Cronet/tests/cts/Android.bp
+++ /dev/null
@@ -1,71 +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_team: "trendy_team_fwk_core_networking",
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_library {
- name: "CtsNetHttpTestsLib",
- defaults: [
- "cts_defaults",
- ],
- sdk_version: "test_current",
- min_sdk_version: "30",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- static_libs: [
- "androidx.test.ext.junit",
- "ctstestrunner-axt",
- "ctstestserver",
- "hamcrest-library",
- "junit",
- "kotlin-test",
- "mockito-target",
- "net-tests-utils",
- "truth",
- ],
- libs: [
- "android.test.base",
- "androidx.annotation_annotation",
- "framework-connectivity",
- "org.apache.http.legacy",
- ],
- lint: {
- test: true,
- },
-}
-
-android_test {
- name: "CtsNetHttpTestCases",
- defaults: [
- "cts_defaults",
- ],
- enforce_default_target_sdk_version: true,
- sdk_version: "test_current",
- min_sdk_version: "30",
- static_libs: ["CtsNetHttpTestsLib"],
- // Tag this as a cts test artifact
- test_suites: [
- "cts",
- "general-tests",
- "mts-tethering",
- "mcts-tethering",
- ],
-}
diff --git a/Cronet/tests/cts/AndroidManifest.xml b/Cronet/tests/cts/AndroidManifest.xml
deleted file mode 100644
index 26900b2..0000000
--- a/Cronet/tests/cts/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +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="android.net.http.cts">
-
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
- <application android:networkSecurityConfig="@xml/network_security_config">
- <uses-library android:name="android.test.runner"/>
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.net.http.cts"
- android:label="CTS tests of android.net.http">
- </instrumentation>
-</manifest>
diff --git a/Cronet/tests/cts/AndroidTest.xml b/Cronet/tests/cts/AndroidTest.xml
deleted file mode 100644
index e0421fd..0000000
--- a/Cronet/tests/cts/AndroidTest.xml
+++ /dev/null
@@ -1,38 +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.
- -->
-<configuration description="Config for CTS Cronet test cases">
- <option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="networking" />
- <!-- Instant apps cannot create sockets. See b/264248246 -->
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
- <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="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 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" />
- </object>
-</configuration>
diff --git a/Cronet/tests/cts/assets/html/hello_world.html b/Cronet/tests/cts/assets/html/hello_world.html
deleted file mode 100644
index ea62ce2..0000000
--- a/Cronet/tests/cts/assets/html/hello_world.html
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<html>
-<head>
- <title>hello world</title>
-</head>
-<body>
-<h3>hello world</h3><br>
-</body>
-</html>
\ No newline at end of file
diff --git a/Cronet/tests/cts/res/xml/network_security_config.xml b/Cronet/tests/cts/res/xml/network_security_config.xml
deleted file mode 100644
index 7d7530b..0000000
--- a/Cronet/tests/cts/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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.
- -->
-
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
\ No newline at end of file
diff --git a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt b/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
deleted file mode 100644
index 464862d..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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 android.content.Context
-import android.net.http.BidirectionalStream
-import android.net.http.HttpEngine
-import android.net.http.cts.util.TestBidirectionalStreamCallback
-import android.net.http.cts.util.TestBidirectionalStreamCallback.ResponseStep
-import android.net.http.cts.util.assumeOKStatusCode
-import android.net.http.cts.util.skipIfNoInternetConnection
-import android.os.Build
-import androidx.test.core.app.ApplicationProvider
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.SkipPresubmit
-import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import org.hamcrest.MatcherAssert
-import org.hamcrest.Matchers
-import org.junit.After
-import org.junit.AssumptionViolatedException
-import org.junit.Before
-import org.junit.runner.RunWith
-
-private const val URL = "https://source.android.com"
-
-/**
- * This tests uses a non-hermetic server. Instead of asserting, assume the next callback. This way,
- * if the request were to fail, the test would just be skipped instead of failing.
- */
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class BidirectionalStreamTest {
- private val context: Context = ApplicationProvider.getApplicationContext()
- private val callback = TestBidirectionalStreamCallback()
- private val httpEngine = HttpEngine.Builder(context).build()
- private var stream: BidirectionalStream? = null
-
- @Before
- fun setUp() {
- skipIfNoInternetConnection(context)
- }
-
- @After
- @Throws(Exception::class)
- fun tearDown() {
- httpEngine.shutdown()
- }
-
- private fun createBidirectionalStreamBuilder(url: String): BidirectionalStream.Builder {
- return httpEngine.newBidirectionalStreamBuilder(url, callback.executor, callback)
- }
-
- @Test
- @Throws(Exception::class)
- @SkipPresubmit(reason = "b/293141085 Confirm non-flaky and move to presubmit after SLO")
- fun testBidirectionalStream_GetStream_CompletesSuccessfully() {
- stream = createBidirectionalStreamBuilder(URL).setHttpMethod("GET").build()
- stream!!.start()
- // We call to a real server and hence the server may not be reachable, cancel this stream
- // and rethrow the exception before tearDown,
- // otherwise shutdown would fail with active request error.
- try {
- callback.assumeCallback(ResponseStep.ON_SUCCEEDED)
- } catch (e: AssumptionViolatedException) {
- stream!!.cancel()
- callback.blockForDone()
- throw e
- }
-
- val info = callback.mResponseInfo
- assumeOKStatusCode(info)
- MatcherAssert.assertThat(
- "Received byte count must be > 0", info.receivedByteCount, Matchers.greaterThan(0L))
- assertEquals("h2", info.negotiatedProtocol)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getHttpMethod() {
- val builder = createBidirectionalStreamBuilder(URL)
- val method = "GET"
-
- builder.setHttpMethod(method)
- stream = builder.build()
- assertThat(stream!!.getHttpMethod()).isEqualTo(method)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_hasTrafficStatsTag() {
- val builder = createBidirectionalStreamBuilder(URL)
-
- builder.setTrafficStatsTag(10)
- stream = builder.build()
- assertThat(stream!!.hasTrafficStatsTag()).isTrue()
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getTrafficStatsTag() {
- val builder = createBidirectionalStreamBuilder(URL)
- val trafficStatsTag = 10
-
- builder.setTrafficStatsTag(trafficStatsTag)
- stream = builder.build()
- assertThat(stream!!.getTrafficStatsTag()).isEqualTo(trafficStatsTag)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_hasTrafficStatsUid() {
- val builder = createBidirectionalStreamBuilder(URL)
-
- builder.setTrafficStatsUid(10)
- stream = builder.build()
- assertThat(stream!!.hasTrafficStatsUid()).isTrue()
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getTrafficStatsUid() {
- val builder = createBidirectionalStreamBuilder(URL)
- val trafficStatsUid = 10
-
- builder.setTrafficStatsUid(trafficStatsUid)
- stream = builder.build()
- assertThat(stream!!.getTrafficStatsUid()).isEqualTo(trafficStatsUid)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getHeaders_asList() {
- val builder = createBidirectionalStreamBuilder(URL)
- val expectedHeaders = mapOf(
- "Authorization" to "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
- "Max-Forwards" to "10",
- "X-Client-Data" to "random custom header content").entries.toList()
-
- for (header in expectedHeaders) {
- builder.addHeader(header.key, header.value)
- }
-
- stream = builder.build()
- assertThat(stream!!.getHeaders().getAsList()).containsAtLeastElementsIn(expectedHeaders)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getHeaders_asMap() {
- val builder = createBidirectionalStreamBuilder(URL)
- val expectedHeaders = mapOf(
- "Authorization" to listOf("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
- "Max-Forwards" to listOf("10"),
- "X-Client-Data" to listOf("random custom header content"))
-
- for (header in expectedHeaders) {
- builder.addHeader(header.key, header.value.get(0))
- }
-
- stream = builder.build()
- assertThat(stream!!.getHeaders().getAsMap()).containsAtLeastEntriesIn(expectedHeaders)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_getPriority() {
- val builder = createBidirectionalStreamBuilder(URL)
- val priority = BidirectionalStream.STREAM_PRIORITY_LOW
-
- builder.setPriority(priority)
- stream = builder.build()
- assertThat(stream!!.getPriority()).isEqualTo(priority)
- }
-
- @Test
- @Throws(Exception::class)
- fun testBidirectionalStream_isDelayRequestHeadersUntilFirstFlushEnabled() {
- val builder = createBidirectionalStreamBuilder(URL)
-
- builder.setDelayRequestHeadersUntilFirstFlushEnabled(true)
- stream = builder.build()
- assertThat(stream!!.isDelayRequestHeadersUntilFirstFlushEnabled()).isTrue()
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/CallbackExceptionTest.kt b/Cronet/tests/cts/src/android/net/http/cts/CallbackExceptionTest.kt
deleted file mode 100644
index 1405ed9..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/CallbackExceptionTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 android.content.Context
-import android.net.http.CallbackException
-import android.net.http.HttpEngine
-import android.net.http.cts.util.HttpCtsTestServer
-import android.net.http.cts.util.TestUrlRequestCallback
-import android.net.http.cts.util.TestUrlRequestCallback.FailureType
-import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep
-import android.os.Build
-import androidx.test.core.app.ApplicationProvider
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertIs
-import kotlin.test.assertSame
-import kotlin.test.assertTrue
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class CallbackExceptionTest {
-
- @Test
- fun testCallbackException_returnsInputParameters() {
- val message = "failed"
- val cause = Throwable("exception")
- val callbackException = object : CallbackException(message, cause) {}
-
- assertEquals(message, callbackException.message)
- assertSame(cause, callbackException.cause)
- }
-
- @Test
- fun testCallbackException_thrownFromUrlRequest() {
- val context: Context = ApplicationProvider.getApplicationContext()
- val server = HttpCtsTestServer(context)
- val httpEngine = HttpEngine.Builder(context).build()
- val callback = TestUrlRequestCallback()
- callback.setFailure(FailureType.THROW_SYNC, ResponseStep.ON_RESPONSE_STARTED)
- val request = httpEngine
- .newUrlRequestBuilder(server.successUrl, callback.executor, callback)
- .build()
-
- request.start()
- callback.blockForDone()
-
- assertTrue(request.isDone)
- assertIs<CallbackException>(callback.mError)
- server.shutdown()
- httpEngine.shutdown()
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
deleted file mode 100644
index 10c7f3c..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 android.net.http.ConnectionMigrationOptions
-import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED
-import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_UNSPECIFIED
-import android.os.Build
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class ConnectionMigrationOptionsTest {
-
- @Test
- fun testConnectionMigrationOptions_defaultValues() {
- val options =
- ConnectionMigrationOptions.Builder().build()
-
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.allowNonDefaultNetworkUsage)
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.defaultNetworkMigration)
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.pathDegradationMigration)
- }
-
- @Test
- fun testConnectionMigrationOptions_enableDefaultNetworkMigration_returnSetValue() {
- val options =
- ConnectionMigrationOptions.Builder()
- .setDefaultNetworkMigration(MIGRATION_OPTION_ENABLED)
- .build()
-
- assertEquals(MIGRATION_OPTION_ENABLED, options.defaultNetworkMigration)
- }
-
- @Test
- fun testConnectionMigrationOptions_enablePathDegradationMigration_returnSetValue() {
- val options =
- ConnectionMigrationOptions.Builder()
- .setPathDegradationMigration(MIGRATION_OPTION_ENABLED)
- .build()
-
- assertEquals(MIGRATION_OPTION_ENABLED, options.pathDegradationMigration)
- }
-
- @Test
- fun testConnectionMigrationOptions_allowNonDefaultNetworkUsage_returnSetValue() {
- val options =
- ConnectionMigrationOptions.Builder()
- .setAllowNonDefaultNetworkUsage(MIGRATION_OPTION_ENABLED).build()
-
- assertEquals(MIGRATION_OPTION_ENABLED, options.allowNonDefaultNetworkUsage)
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt
deleted file mode 100644
index 56802c6..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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 android.net.http.DnsOptions
-import android.net.http.DnsOptions.DNS_OPTION_ENABLED
-import android.net.http.DnsOptions.DNS_OPTION_UNSPECIFIED
-import android.os.Build
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import java.time.Duration
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class DnsOptionsTest {
-
- @Test
- fun testDnsOptions_defaultValues() {
- val options = DnsOptions.Builder().build()
-
- assertEquals(DNS_OPTION_UNSPECIFIED, options.persistHostCache)
- assertNull(options.persistHostCachePeriod)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.staleDns)
- assertNull(options.staleDnsOptions)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.useHttpStackDnsResolver)
- assertEquals(DNS_OPTION_UNSPECIFIED,
- options.preestablishConnectionsToStaleDnsResults)
- }
-
- @Test
- fun testDnsOptions_persistHostCache_returnSetValue() {
- val options = DnsOptions.Builder()
- .setPersistHostCache(DNS_OPTION_ENABLED)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED, options.persistHostCache)
- }
-
- @Test
- fun testDnsOptions_persistHostCachePeriod_returnSetValue() {
- val period = Duration.ofMillis(12345)
- val options = DnsOptions.Builder().setPersistHostCachePeriod(period).build()
-
- assertEquals(period, options.persistHostCachePeriod)
- }
-
- @Test
- fun testDnsOptions_enableStaleDns_returnSetValue() {
- val options = DnsOptions.Builder()
- .setStaleDns(DNS_OPTION_ENABLED)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED, options.staleDns)
- }
-
- @Test
- fun testDnsOptions_useHttpStackDnsResolver_returnsSetValue() {
- val options = DnsOptions.Builder()
- .setUseHttpStackDnsResolver(DNS_OPTION_ENABLED)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED, options.useHttpStackDnsResolver)
- }
-
- @Test
- fun testDnsOptions_preestablishConnectionsToStaleDnsResults_returnsSetValue() {
- val options = DnsOptions.Builder()
- .setPreestablishConnectionsToStaleDnsResults(DNS_OPTION_ENABLED)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED,
- options.preestablishConnectionsToStaleDnsResults)
- }
-
- @Test
- fun testDnsOptions_setStaleDnsOptions_returnsSetValues() {
- val staleOptions = DnsOptions.StaleDnsOptions.Builder()
- .setAllowCrossNetworkUsage(DNS_OPTION_ENABLED)
- .setFreshLookupTimeout(Duration.ofMillis(1234))
- .build()
- val options = DnsOptions.Builder()
- .setStaleDns(DNS_OPTION_ENABLED)
- .setStaleDnsOptions(staleOptions)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED, options.staleDns)
- assertEquals(staleOptions, options.staleDnsOptions)
- }
-
- @Test
- fun testStaleDnsOptions_defaultValues() {
- val options = DnsOptions.StaleDnsOptions.Builder().build()
-
- assertEquals(DNS_OPTION_UNSPECIFIED, options.allowCrossNetworkUsage)
- assertNull(options.freshLookupTimeout)
- assertNull(options.maxExpiredDelay)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.useStaleOnNameNotResolved)
- }
-
- @Test
- fun testStaleDnsOptions_allowCrossNetworkUsage_returnsSetValue() {
- val options = DnsOptions.StaleDnsOptions.Builder()
- .setAllowCrossNetworkUsage(DNS_OPTION_ENABLED).build()
-
- assertEquals(DNS_OPTION_ENABLED, options.allowCrossNetworkUsage)
- }
-
- @Test
- fun testStaleDnsOptions_freshLookupTimeout_returnsSetValue() {
- val duration = Duration.ofMillis(12345)
- val options = DnsOptions.StaleDnsOptions.Builder().setFreshLookupTimeout(duration).build()
-
- assertNotNull(options.freshLookupTimeout)
- assertEquals(duration, options.freshLookupTimeout!!)
- }
-
- @Test
- fun testStaleDnsOptions_useStaleOnNameNotResolved_returnsSetValue() {
- val options = DnsOptions.StaleDnsOptions.Builder()
- .setUseStaleOnNameNotResolved(DNS_OPTION_ENABLED)
- .build()
-
- assertEquals(DNS_OPTION_ENABLED, options.useStaleOnNameNotResolved)
- }
-
- @Test
- fun testStaleDnsOptions_maxExpiredDelayMillis_returnsSetValue() {
- val duration = Duration.ofMillis(12345)
- val options = DnsOptions.StaleDnsOptions.Builder().setMaxExpiredDelay(duration).build()
-
- assertNotNull(options.maxExpiredDelay)
- assertEquals(duration, options.maxExpiredDelay!!)
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
deleted file mode 100644
index f86ac29..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * 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.assumeOKStatusCode;
-import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.net.Network;
-import android.net.http.ConnectionMigrationOptions;
-import android.net.http.DnsOptions;
-import android.net.http.HttpEngine;
-import android.net.http.QuicOptions;
-import android.net.http.UrlRequest;
-import android.net.http.UrlResponseInfo;
-import android.net.http.cts.util.HttpCtsTestServer;
-import android.net.http.cts.util.TestUrlRequestCallback;
-import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
-import android.os.Build;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Set;
-
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-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 HttpCtsTestServer mTestServer;
- private UrlRequest mRequest;
- private HttpEngine mEngine;
- private Context mContext;
-
- @Before
- public void setUp() throws Exception {
- mContext = ApplicationProvider.getApplicationContext();
- skipIfNoInternetConnection(mContext);
- mEngineBuilder = new HttpEngine.Builder(mContext);
- mCallback = new TestUrlRequestCallback();
- mTestServer = new HttpCtsTestServer(mContext);
- }
-
- @After
- public void tearDown() throws Exception {
- if (mRequest != null) {
- mRequest.cancel();
- mCallback.blockForDone();
- }
- if (mEngine != null) {
- mEngine.shutdown();
- }
- if (mTestServer != null) {
- mTestServer.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.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- // This tests uses a non-hermetic server. Instead of asserting, assume the next callback.
- // This way, if the request were to fail, the test would just be skipped instead of failing.
- mCallback.assumeCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- assertEquals("h2", info.getNegotiatedProtocol());
- }
-
- @Test
- public void testHttpEngine_EnableHttpCache() {
- String url = mTestServer.getCacheableTestDownloadUrl(
- /* downloadId */ "cacheable-download",
- /* numBytes */ 10);
- mEngine =
- mEngineBuilder
- .setStoragePath(mContext.getApplicationInfo().dataDir)
- .setEnableHttpCache(
- HttpEngine.Builder.HTTP_CACHE_DISK, /* maxSize */ 100 * 1024)
- .build();
-
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assumeOKStatusCode(info);
- assertFalse(info.wasCached());
-
- mCallback = new TestUrlRequestCallback();
- builder = mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- assertTrue(info.wasCached());
- }
-
- @Test
- public void testHttpEngine_DisableHttp2() throws Exception {
- mEngine = mEngineBuilder.setEnableHttp2(false).build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- // This tests uses a non-hermetic server. Instead of asserting, assume the next callback.
- // This way, if the request were to fail, the test would just be skipped instead of failing.
- mCallback.assumeCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- assertEquals("http/1.1", info.getNegotiatedProtocol());
- }
-
- @Test
- public void testHttpEngine_EnablePublicKeyPinningBypassForLocalTrustAnchors() {
- String url = mTestServer.getSuccessUrl();
- // For known hosts, requests should succeed whether we're bypassing the local trust anchor
- // or not.
- mEngine = mEngineBuilder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false).build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- mEngine.shutdown();
- mEngine = mEngineBuilder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(true).build();
- mCallback = new TestUrlRequestCallback();
- builder = mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- // TODO(b/270918920): We should also test with a certificate not present in the device's
- // trusted store.
- // This requires either:
- // * Mocking the underlying CertificateVerifier.
- // * Or, having the server return a root certificate not present in the device's trusted
- // store.
- // The former doesn't make sense for a CTS test as it would depend on the underlying
- // implementation. The latter is something we should support once we write a proper test
- // server.
- }
-
- private byte[] generateSha256() {
- byte[] sha256 = new byte[32];
- Arrays.fill(sha256, (byte) 58);
- return sha256;
- }
-
- private Instant instantInFuture(int secondsIntoFuture) {
- Calendar cal = Calendar.getInstance();
- cal.add(Calendar.SECOND, secondsIntoFuture);
- return cal.getTime().toInstant();
- }
-
- @Test
- public void testHttpEngine_AddPublicKeyPins() {
- // CtsTestServer, when set in SslMode.NO_CLIENT_AUTH (required to trigger
- // certificate verification, needed by this test), uses a certificate that
- // doesn't match the hostname. For this reason, CtsTestServer cannot be used
- // by this test.
- Instant expirationInstant = instantInFuture(/* secondsIntoFuture */ 100);
- boolean includeSubdomains = true;
- Set<byte[]> pinsSha256 = Set.of(generateSha256());
- mEngine = mEngineBuilder.addPublicKeyPins(
- HOST, pinsSha256, includeSubdomains, expirationInstant).build();
-
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.expectCallback(ResponseStep.ON_FAILED);
- assertNotNull("Expected an error", mCallback.mError);
- }
-
- @Test
- public void testHttpEngine_EnableQuic() throws Exception {
- String url = mTestServer.getSuccessUrl();
- mEngine = mEngineBuilder.setEnableQuic(true).addQuicHint(HOST, 443, 443).build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- }
-
- @Test
- public void testHttpEngine_GetDefaultUserAgent() throws Exception {
- assertThat(mEngineBuilder.getDefaultUserAgent(), containsString("AndroidHttpClient"));
- assertThat(mEngineBuilder.getDefaultUserAgent()).contains(HttpEngine.getVersionString());
- }
-
- @Test
- public void testHttpEngine_requestUsesDefaultUserAgent() throws Exception {
- mEngine = mEngineBuilder.build();
-
- String url = mTestServer.getUserAgentUrl();
- UrlRequest request =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback).build();
- request.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- String receivedUserAgent = extractUserAgent(mCallback.mResponseAsString);
-
- assertThat(receivedUserAgent).isEqualTo(mEngineBuilder.getDefaultUserAgent());
- }
-
- @Test
- public void testHttpEngine_requestUsesCustomUserAgent() throws Exception {
- String userAgent = "CtsTests User Agent";
- mEngine =
- new HttpEngine.Builder(ApplicationProvider.getApplicationContext())
- .setUserAgent(userAgent)
- .build();
-
- String url = mTestServer.getUserAgentUrl();
- UrlRequest request =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback).build();
- request.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- String receivedUserAgent = extractUserAgent(mCallback.mResponseAsString);
-
- assertThat(receivedUserAgent).isEqualTo(userAgent);
- }
-
- private static String extractUserAgent(String userAgentResponseBody) {
- // If someone wants to be evil and have the title HTML tag a part of the user agent,
- // they'll have to fix this method :)
- return userAgentResponseBody.replaceFirst(".*<title>", "").replaceFirst("</title>.*", "");
- }
-
- @Test
- public void testHttpEngine_bindToNetwork() throws Exception {
- // Create a fake Android.net.Network. Since that network doesn't exist, binding to
- // that should end up in a failed request.
- Network mockNetwork = Mockito.mock(Network.class);
- Mockito.when(mockNetwork.getNetworkHandle()).thenReturn(123L);
- String url = mTestServer.getSuccessUrl();
-
- mEngine = mEngineBuilder.build();
- mEngine.bindToNetwork(mockNetwork);
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_FAILED);
- }
-
- @Test
- public void testHttpEngine_unbindFromNetwork() throws Exception {
- // Create a fake Android.net.Network. Since that network doesn't exist, binding to
- // that should end up in a failed request.
- Network mockNetwork = Mockito.mock(Network.class);
- Mockito.when(mockNetwork.getNetworkHandle()).thenReturn(123L);
- String url = mTestServer.getSuccessUrl();
-
- mEngine = mEngineBuilder.build();
- // Bind to the fake network but then unbind. This should result in a successful
- // request.
- mEngine.bindToNetwork(mockNetwork);
- mEngine.bindToNetwork(null);
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- }
-
- @Test
- public void testHttpEngine_setConnectionMigrationOptions_requestSucceeds() {
- ConnectionMigrationOptions options = new ConnectionMigrationOptions.Builder().build();
- mEngine = mEngineBuilder.setConnectionMigrationOptions(options).build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(
- mTestServer.getSuccessUrl(), mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- }
-
- @Test
- public void testHttpEngine_setDnsOptions_requestSucceeds() {
- DnsOptions options = new DnsOptions.Builder().build();
- mEngine = mEngineBuilder.setDnsOptions(options).build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(
- mTestServer.getSuccessUrl(), mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- }
-
- @Test
- public void getVersionString_notEmpty() {
- assertThat(HttpEngine.getVersionString()).isNotEmpty();
- }
-
- @Test
- public void testHttpEngine_SetQuicOptions_RequestSucceedsWithQuic() throws Exception {
- String url = mTestServer.getSuccessUrl();
- QuicOptions options = new QuicOptions.Builder().build();
- mEngine = mEngineBuilder
- .setEnableQuic(true)
- .addQuicHint(HOST, 443, 443)
- .setQuicOptions(options)
- .build();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
-
- }
-
- @Test
- public void testHttpEngine_enableBrotli_brotliAdvertised() {
- mEngine = mEngineBuilder.setEnableBrotli(true).build();
- mRequest =
- mEngine.newUrlRequestBuilder(
- mTestServer.getEchoHeadersUrl(), mCallback.getExecutor(), mCallback)
- .build();
- mRequest.start();
-
- mCallback.assumeCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertThat(info.getHeaders().getAsMap().get("x-request-header-Accept-Encoding").toString())
- .contains("br");
- assertOKStatusCode(info);
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/NetworkExceptionTest.kt b/Cronet/tests/cts/src/android/net/http/cts/NetworkExceptionTest.kt
deleted file mode 100644
index cff54b3..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/NetworkExceptionTest.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 android.net.http.HttpEngine
-import android.net.http.NetworkException
-import android.net.http.cts.util.TestUrlRequestCallback
-import android.os.Build
-import androidx.test.core.app.ApplicationProvider
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.assertEquals
-import kotlin.test.assertIs
-import kotlin.test.assertSame
-import kotlin.test.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class NetworkExceptionTest {
-
- @Test
- fun testNetworkException_returnsInputParameters() {
- val message = "failed"
- val cause = Throwable("thrown")
- val networkException =
- object : NetworkException(message, cause) {
- override fun getErrorCode() = 0
- override fun isImmediatelyRetryable() = false
- }
-
- assertEquals(message, networkException.message)
- assertSame(cause, networkException.cause)
- }
-
- @Test
- fun testNetworkException_thrownFromUrlRequest() {
- val httpEngine = HttpEngine.Builder(ApplicationProvider.getApplicationContext()).build()
- val callback = TestUrlRequestCallback()
- val request =
- httpEngine.newUrlRequestBuilder("http://localhost", callback.executor, callback).build()
-
- request.start()
- callback.blockForDone()
-
- assertTrue(request.isDone)
- assertIs<NetworkException>(callback.mError)
- httpEngine.shutdown()
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt
deleted file mode 100644
index 2705fc3..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 android.net.http.QuicException
-import android.os.Build
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class QuicExceptionTest {
-
- @Test
- fun testQuicException_returnsInputParameters() {
- val message = "failed"
- val cause = Throwable("thrown")
- val quicException =
- object : QuicException(message, cause) {
- override fun getErrorCode() = 0
- override fun isImmediatelyRetryable() = false
- }
-
- assertEquals(message, quicException.message)
- assertEquals(cause, quicException.cause)
- }
-
- // TODO: add test for QuicException triggered from HttpEngine
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
deleted file mode 100644
index da0b15c..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 android.net.http.QuicOptions
-import android.os.Build
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import com.google.common.truth.Truth.assertThat
-import java.time.Duration
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class QuicOptionsTest {
- @Test
- fun testQuicOptions_defaultValues() {
- val quicOptions = QuicOptions.Builder().build()
- assertThat(quicOptions.allowedQuicHosts).isEmpty()
- assertThat(quicOptions.handshakeUserAgent).isNull()
- assertThat(quicOptions.idleConnectionTimeout).isNull()
- assertFalse(quicOptions.hasInMemoryServerConfigsCacheSize())
- assertFailsWith(IllegalStateException::class) {
- quicOptions.inMemoryServerConfigsCacheSize
- }
- }
-
- @Test
- fun testQuicOptions_quicHostAllowlist_returnsAddedValues() {
- val quicOptions = QuicOptions.Builder()
- .addAllowedQuicHost("foo")
- .addAllowedQuicHost("bar")
- .addAllowedQuicHost("foo")
- .addAllowedQuicHost("baz")
- .build()
- assertThat(quicOptions.allowedQuicHosts)
- .containsExactly("foo", "bar", "baz")
- .inOrder()
- }
-
- @Test
- fun testQuicOptions_idleConnectionTimeout_returnsSetValue() {
- val timeout = Duration.ofMinutes(10)
- val quicOptions = QuicOptions.Builder()
- .setIdleConnectionTimeout(timeout)
- .build()
- assertThat(quicOptions.idleConnectionTimeout)
- .isEqualTo(timeout)
- }
-
- @Test
- fun testQuicOptions_inMemoryServerConfigsCacheSize_returnsSetValue() {
- val quicOptions = QuicOptions.Builder()
- .setInMemoryServerConfigsCacheSize(42)
- .build()
- assertTrue(quicOptions.hasInMemoryServerConfigsCacheSize())
- assertThat(quicOptions.inMemoryServerConfigsCacheSize)
- .isEqualTo(42)
- }
-
- @Test
- fun testQuicOptions_handshakeUserAgent_returnsSetValue() {
- val userAgent = "test"
- val quicOptions = QuicOptions.Builder()
- .setHandshakeUserAgent(userAgent)
- .build()
- assertThat(quicOptions.handshakeUserAgent)
- .isEqualTo(userAgent)
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
deleted file mode 100644
index 3c4d134..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ /dev/null
@@ -1,526 +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 android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
-import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.greaterThan;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.net.http.HeaderBlock;
-import android.net.http.HttpEngine;
-import android.net.http.HttpException;
-import android.net.http.InlineExecutionProhibitedException;
-import android.net.http.UploadDataProvider;
-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 android.net.http.cts.util.UploadDataProviders;
-import android.os.Build;
-import android.webkit.cts.CtsTestServer;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.test.core.app.ApplicationProvider;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
-
-import com.google.common.base.Strings;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.URLEncoder;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-public class UrlRequestTest {
- private static final Executor DIRECT_EXECUTOR = Runnable::run;
-
- private TestUrlRequestCallback mCallback;
- private HttpCtsTestServer mTestServer;
- private HttpEngine mHttpEngine;
-
- @Before
- public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
- 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.Builder createUrlRequestBuilder(String url) {
- return mHttpEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
- }
-
- @Test
- public void testUrlRequestGet_CompletesSuccessfully() throws Exception {
- String url = mTestServer.getSuccessUrl();
- UrlRequest request = createUrlRequestBuilder(url).build();
- 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 = createUrlRequestBuilder(mTestServer.getSuccessUrl()).build();
- // 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);
- }
-
- @Test
- public void testUrlRequestCancel_CancelCalled() throws Exception {
- UrlRequest request = createUrlRequestBuilder(mTestServer.getSuccessUrl()).build();
- mCallback.setAutoAdvance(false);
-
- request.start();
- mCallback.waitForNextStep();
- assertSame(mCallback.mResponseStep, ResponseStep.ON_RESPONSE_STARTED);
-
- request.cancel();
- mCallback.expectCallback(ResponseStep.ON_CANCELED);
- }
-
- @Test
- public void testUrlRequestPost_EchoRequestBody() {
- String testData = "test";
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl());
-
- UploadDataProvider dataProvider = UploadDataProviders.create(testData);
- builder.setUploadDataProvider(dataProvider, mCallback.getExecutor());
- builder.addHeader("Content-Type", "text/html");
- builder.build().start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- assertOKStatusCode(mCallback.mResponseInfo);
- assertEquals(testData, mCallback.mResponseAsString);
- }
-
- @Test
- public void testUrlRequestFail_FailedCalled() {
- createUrlRequestBuilder("http://0.0.0.0:0/").build().start();
- mCallback.expectCallback(ResponseStep.ON_FAILED);
- }
-
- @Test
- public void testUrlRequest_directExecutor_allowed() throws InterruptedException {
- TestUrlRequestCallback callback = new TestUrlRequestCallback();
- callback.setAllowDirectExecutor(true);
- UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
- mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = UploadDataProviders.create("test");
- builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR);
- builder.addHeader("Content-Type", "text/plain;charset=UTF-8");
- builder.setDirectExecutorAllowed(true);
- builder.build().start();
- callback.blockForDone();
-
- if (callback.mOnErrorCalled) {
- throw new AssertionError("Expected no exception", callback.mError);
- }
-
- assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
- assertEquals("test", callback.mResponseAsString);
- }
-
- @Test
- public void testUrlRequest_directExecutor_disallowed_uploadDataProvider() throws Exception {
- TestUrlRequestCallback callback = new TestUrlRequestCallback();
- // This applies just locally to the test callback, not to SUT
- callback.setAllowDirectExecutor(true);
-
- UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
- mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback);
- UploadDataProvider dataProvider = UploadDataProviders.create("test");
-
- builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR)
- .addHeader("Content-Type", "text/plain;charset=UTF-8")
- .build()
- .start();
- callback.blockForDone();
-
- assertTrue(callback.mOnErrorCalled);
- assertTrue(callback.mError.getCause() instanceof InlineExecutionProhibitedException);
- }
-
- @Test
- public void testUrlRequest_directExecutor_disallowed_responseCallback() throws Exception {
- TestUrlRequestCallback callback = new TestUrlRequestCallback();
- // This applies just locally to the test callback, not to SUT
- callback.setAllowDirectExecutor(true);
-
- UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
- mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = UploadDataProviders.create("test");
-
- builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor())
- .addHeader("Content-Type", "text/plain;charset=UTF-8")
- .build()
- .start();
- callback.blockForDone();
-
- assertTrue(callback.mOnErrorCalled);
- assertTrue(callback.mError.getCause() instanceof InlineExecutionProhibitedException);
- }
-
- @Test
- public void testUrlRequest_nonDirectByteBuffer() throws Exception {
- BlockingQueue<HttpException> onFailedException = new ArrayBlockingQueue<>(1);
-
- UrlRequest request =
- mHttpEngine
- .newUrlRequestBuilder(
- mTestServer.getSuccessUrl(),
- Executors.newSingleThreadExecutor(),
- new StubUrlRequestCallback() {
- @Override
- public void onResponseStarted(
- UrlRequest request, UrlResponseInfo info) {
- // note: allocate, not allocateDirect
- request.read(ByteBuffer.allocate(1024));
- }
-
- @Override
- public void onFailed(
- UrlRequest request,
- UrlResponseInfo info,
- HttpException error) {
- onFailedException.add(error);
- }
- })
- .build();
- request.start();
-
- HttpException e = onFailedException.poll(5, TimeUnit.SECONDS);
- assertNotNull(e);
- assertTrue(e.getCause() instanceof IllegalArgumentException);
- assertTrue(e.getCause().getMessage().contains("direct"));
- }
-
- @Test
- public void testUrlRequest_fullByteBuffer() throws Exception {
- BlockingQueue<HttpException> onFailedException = new ArrayBlockingQueue<>(1);
-
- UrlRequest request =
- mHttpEngine
- .newUrlRequestBuilder(
- mTestServer.getSuccessUrl(),
- Executors.newSingleThreadExecutor(),
- new StubUrlRequestCallback() {
- @Override
- public void onResponseStarted(
- UrlRequest request, UrlResponseInfo info) {
- ByteBuffer bb = ByteBuffer.allocateDirect(1024);
- bb.position(bb.limit());
- request.read(bb);
- }
-
- @Override
- public void onFailed(
- UrlRequest request,
- UrlResponseInfo info,
- HttpException error) {
- onFailedException.add(error);
- }
- })
- .build();
- request.start();
-
- HttpException e = onFailedException.poll(5, TimeUnit.SECONDS);
- assertNotNull(e);
- assertTrue(e.getCause() instanceof IllegalArgumentException);
- assertTrue(e.getCause().getMessage().contains("full"));
- }
-
- @Test
- public void testUrlRequest_redirects() throws Exception {
- int expectedNumRedirects = 5;
- String url =
- mTestServer.getRedirectingAssetUrl("html/hello_world.html", expectedNumRedirects);
-
- UrlRequest request = createUrlRequestBuilder(url).build();
- request.start();
-
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
- UrlResponseInfo info = mCallback.mResponseInfo;
- assertOKStatusCode(info);
- assertThat(mCallback.mResponseAsString).contains("hello world");
- assertThat(info.getUrlChain()).hasSize(expectedNumRedirects + 1);
- assertThat(info.getUrlChain().get(0)).isEqualTo(url);
- assertThat(info.getUrlChain().get(expectedNumRedirects)).isEqualTo(info.getUrl());
- }
-
- @Test
- public void testUrlRequestPost_withRedirect() throws Exception {
- String body = Strings.repeat(
- "Hello, this is a really interesting body, so write this 100 times.", 100);
-
- String redirectUrlParameter =
- URLEncoder.encode(mTestServer.getEchoBodyUrl(), "UTF-8");
- createUrlRequestBuilder(
- String.format(
- "%s/alt_redirect?dest=%s&statusCode=307",
- mTestServer.getBaseUri(),
- redirectUrlParameter))
- .setHttpMethod("POST")
- .addHeader("Content-Type", "text/plain")
- .setUploadDataProvider(
- UploadDataProviders.create(body.getBytes(StandardCharsets.UTF_8)),
- mCallback.getExecutor())
- .build()
- .start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- assertOKStatusCode(mCallback.mResponseInfo);
- assertThat(mCallback.mResponseAsString).isEqualTo(body);
- }
-
- @Test
- public void testUrlRequest_customHeaders() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoHeadersUrl());
-
- List<Map.Entry<String, String>> expectedHeaders = Arrays.asList(
- Map.entry("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
- Map.entry("Max-Forwards", "10"),
- Map.entry("X-Client-Data", "random custom header content"));
-
- for (Map.Entry<String, String> header : expectedHeaders) {
- builder.addHeader(header.getKey(), header.getValue());
- }
-
- builder.build().start();
- mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
-
- assertOKStatusCode(mCallback.mResponseInfo);
-
- List<Map.Entry<String, String>> echoedHeaders =
- extractEchoedHeaders(mCallback.mResponseInfo.getHeaders());
-
- // The implementation might decide to add more headers like accepted encodings it handles
- // internally so the server is likely to see more headers than explicitly set
- // by the developer.
- assertThat(echoedHeaders)
- .containsAtLeastElementsIn(expectedHeaders);
- }
-
- @Test
- public void testUrlRequest_getHttpMethod() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final String method = "POST";
-
- builder.setHttpMethod(method);
- UrlRequest request = builder.build();
- assertThat(request.getHttpMethod()).isEqualTo(method);
- }
-
- @Test
- public void testUrlRequest_getHeaders_asList() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final List<Map.Entry<String, String>> expectedHeaders = Arrays.asList(
- Map.entry("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
- Map.entry("Max-Forwards", "10"),
- Map.entry("X-Client-Data", "random custom header content"));
-
- for (Map.Entry<String, String> header : expectedHeaders) {
- builder.addHeader(header.getKey(), header.getValue());
- }
-
- UrlRequest request = builder.build();
- assertThat(request.getHeaders().getAsList()).containsAtLeastElementsIn(expectedHeaders);
- }
-
- @Test
- public void testUrlRequest_getHeaders_asMap() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final Map<String, List<String>> expectedHeaders = Map.of(
- "Authorization", Arrays.asList("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
- "Max-Forwards", Arrays.asList("10"),
- "X-Client-Data", Arrays.asList("random custom header content"));
-
- for (Map.Entry<String, List<String>> header : expectedHeaders.entrySet()) {
- builder.addHeader(header.getKey(), header.getValue().get(0));
- }
-
- UrlRequest request = builder.build();
- assertThat(request.getHeaders().getAsMap()).containsAtLeastEntriesIn(expectedHeaders);
- }
-
- @Test
- public void testUrlRequest_isCacheDisabled() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final boolean isCacheDisabled = true;
-
- builder.setCacheDisabled(isCacheDisabled);
- UrlRequest request = builder.build();
- assertThat(request.isCacheDisabled()).isEqualTo(isCacheDisabled);
- }
-
- @Test
- public void testUrlRequest_isDirectExecutorAllowed() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final boolean isDirectExecutorAllowed = true;
-
- builder.setDirectExecutorAllowed(isDirectExecutorAllowed);
- UrlRequest request = builder.build();
- assertThat(request.isDirectExecutorAllowed()).isEqualTo(isDirectExecutorAllowed);
- }
-
- @Test
- public void testUrlRequest_getPriority() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final int priority = UrlRequest.REQUEST_PRIORITY_LOW;
-
- builder.setPriority(priority);
- UrlRequest request = builder.build();
- assertThat(request.getPriority()).isEqualTo(priority);
- }
-
- @Test
- public void testUrlRequest_hasTrafficStatsTag() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
-
- builder.setTrafficStatsTag(10);
- UrlRequest request = builder.build();
- assertThat(request.hasTrafficStatsTag()).isEqualTo(true);
- }
-
- @Test
- public void testUrlRequest_getTrafficStatsTag() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final int trafficStatsTag = 10;
-
- builder.setTrafficStatsTag(trafficStatsTag);
- UrlRequest request = builder.build();
- assertThat(request.getTrafficStatsTag()).isEqualTo(trafficStatsTag);
- }
-
- @Test
- public void testUrlRequest_hasTrafficStatsUid() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
-
- builder.setTrafficStatsUid(10);
- UrlRequest request = builder.build();
- assertThat(request.hasTrafficStatsUid()).isEqualTo(true);
- }
-
- @Test
- public void testUrlRequest_getTrafficStatsUid() throws Exception {
- UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
- final int trafficStatsUid = 10;
-
- builder.setTrafficStatsUid(trafficStatsUid);
- UrlRequest request = builder.build();
- assertThat(request.getTrafficStatsUid()).isEqualTo(trafficStatsUid);
- }
-
- private static List<Map.Entry<String, String>> extractEchoedHeaders(HeaderBlock headers) {
- return headers.getAsList()
- .stream()
- .flatMap(input -> {
- if (input.getKey().startsWith(CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX)) {
- String strippedKey =
- input.getKey().substring(
- CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX.length());
- return Stream.of(Map.entry(strippedKey, input.getValue()));
- } else {
- return Stream.empty();
- }
- })
- .collect(Collectors.toList());
- }
-
- private static class StubUrlRequestCallback implements UrlRequest.Callback {
-
- @Override
- public void onRedirectReceived(
- UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onReadCompleted(
- UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) {
- throw new UnsupportedOperationException(error);
- }
-
- @Override
- public void onCanceled(@NonNull UrlRequest request, @Nullable UrlResponseInfo info) {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlResponseInfoTest.kt b/Cronet/tests/cts/src/android/net/http/cts/UrlResponseInfoTest.kt
deleted file mode 100644
index f1b57c6..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlResponseInfoTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 android.content.Context
-import android.net.http.HttpEngine
-import android.net.http.cts.util.HttpCtsTestServer
-import android.net.http.cts.util.TestUrlRequestCallback
-import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep
-import android.os.Build
-import androidx.test.core.app.ApplicationProvider
-import com.android.testutils.DevSdkIgnoreRule
-import com.android.testutils.DevSdkIgnoreRunner
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-import org.junit.runner.RunWith
-
-@RunWith(DevSdkIgnoreRunner::class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
-class UrlResponseInfoTest {
-
- @Test
- fun testUrlResponseInfo_apisReturnCorrectInfo() {
- // start the engine and send a request
- val context: Context = ApplicationProvider.getApplicationContext()
- val server = HttpCtsTestServer(context)
- val httpEngine = HttpEngine.Builder(context).build()
- val callback = TestUrlRequestCallback()
- val url = server.successUrl
- val request = httpEngine.newUrlRequestBuilder(url, callback.executor, callback).build()
-
- request.start()
- callback.expectCallback(ResponseStep.ON_SUCCEEDED)
-
- val info = callback.mResponseInfo
- assertFalse(info.headers.asList.isEmpty())
- assertEquals(200, info.httpStatusCode)
- assertTrue(info.receivedByteCount > 0)
- assertEquals(url, info.url)
- assertEquals(listOf(url), info.urlChain)
- assertFalse(info.wasCached())
-
- // TODO Current test server does not set these values. Uncomment when we use one that does.
- // assertEquals("OK", info.httpStatusText)
- // assertEquals("http/1.1", info.negotiatedProtocol)
-
- // cronet defaults to port 0 when no proxy is specified.
- // This is not a behaviour we want to enforce since null is reasonable too.
- // assertEquals(":0", info.proxyServer)
-
- server.shutdown()
- httpEngine.shutdown()
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt b/Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt
deleted file mode 100644
index 5196544..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/HttpCtsTestServer.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.http.cts.util
-
-import android.content.Context
-import android.webkit.cts.CtsTestServer
-import java.net.URI
-import org.apache.http.HttpEntityEnclosingRequest
-import org.apache.http.HttpRequest
-import org.apache.http.HttpResponse
-import org.apache.http.HttpStatus
-import org.apache.http.HttpVersion
-import org.apache.http.message.BasicHttpResponse
-
-private const val ECHO_BODY_PATH = "/echo_body"
-
-/** Extends CtsTestServer to handle POST requests and other test specific requests */
-class HttpCtsTestServer(context: Context) : CtsTestServer(context) {
-
- val echoBodyUrl: String = baseUri + ECHO_BODY_PATH
- val successUrl: String = getAssetUrl("html/hello_world.html")
-
- override fun onPost(req: HttpRequest): HttpResponse? {
- val path = URI.create(req.requestLine.uri).path
- var response: HttpResponse? = null
-
- if (path.startsWith(ECHO_BODY_PATH)) {
- if (req !is HttpEntityEnclosingRequest) {
- return BasicHttpResponse(
- HttpVersion.HTTP_1_0,
- HttpStatus.SC_INTERNAL_SERVER_ERROR,
- "Expected req to be of type HttpEntityEnclosingRequest but got ${req.javaClass}"
- )
- }
-
- response = BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, null)
- response.entity = req.entity
- response.addHeader("Content-Length", req.entity.contentLength.toString())
- }
-
- return response
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestBidirectionalStreamCallback.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestBidirectionalStreamCallback.java
deleted file mode 100644
index 1e7333c..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestBidirectionalStreamCallback.java
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * 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 static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeThat;
-import static org.junit.Assume.assumeTrue;
-
-import android.net.http.BidirectionalStream;
-import android.net.http.HeaderBlock;
-import android.net.http.HttpException;
-import android.net.http.UrlResponseInfo;
-import android.os.ConditionVariable;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * Callback that tracks information from different callbacks and has a method to block thread until
- * the stream completes on another thread. Allows to cancel, block stream or throw an exception from
- * an arbitrary step.
- */
-public class TestBidirectionalStreamCallback implements BidirectionalStream.Callback {
- private static final int TIMEOUT_MS = 12_000;
- public UrlResponseInfo mResponseInfo;
- public HttpException mError;
-
- public ResponseStep mResponseStep = ResponseStep.NOTHING;
-
- public boolean mOnErrorCalled;
- public boolean mOnCanceledCalled;
-
- public int mHttpResponseDataLength;
- public String mResponseAsString = "";
-
- public HeaderBlock mTrailers;
-
- private static final int READ_BUFFER_SIZE = 32 * 1024;
-
- // When false, the consumer is responsible for all calls into the stream
- // that advance it.
- private boolean mAutoAdvance = true;
-
- // Conditionally fail on certain steps.
- private FailureType mFailureType = FailureType.NONE;
- private ResponseStep mFailureStep = ResponseStep.NOTHING;
-
- // Signals when the stream is done either successfully or not.
- private final ConditionVariable mDone = new ConditionVariable();
-
- // Signaled on each step when mAutoAdvance is false.
- private final ConditionVariable mReadStepBlock = new ConditionVariable();
- private final ConditionVariable mWriteStepBlock = new ConditionVariable();
-
- // Executor Service for Cronet callbacks.
- private final ExecutorService mExecutorService =
- Executors.newSingleThreadExecutor(new ExecutorThreadFactory());
- private Thread mExecutorThread;
-
- // position() of ByteBuffer prior to read() call.
- private int mBufferPositionBeforeRead;
-
- // Data to write.
- private final ArrayList<WriteBuffer> mWriteBuffers = new ArrayList<WriteBuffer>();
-
- // Buffers that we yet to receive the corresponding onWriteCompleted callback.
- private final ArrayList<WriteBuffer> mWriteBuffersToBeAcked = new ArrayList<WriteBuffer>();
-
- // Whether to use a direct executor.
- private final boolean mUseDirectExecutor;
- private final DirectExecutor mDirectExecutor;
-
- private class ExecutorThreadFactory implements ThreadFactory {
- @Override
- public Thread newThread(Runnable r) {
- mExecutorThread = new Thread(r);
- return mExecutorThread;
- }
- }
-
- private static class WriteBuffer {
- final ByteBuffer mBuffer;
- final boolean mFlush;
-
- WriteBuffer(ByteBuffer buffer, boolean flush) {
- mBuffer = buffer;
- mFlush = flush;
- }
- }
-
- private static class DirectExecutor implements Executor {
- @Override
- public void execute(Runnable task) {
- task.run();
- }
- }
-
- public enum ResponseStep {
- NOTHING,
- ON_STREAM_READY,
- ON_RESPONSE_STARTED,
- ON_READ_COMPLETED,
- ON_WRITE_COMPLETED,
- ON_TRAILERS,
- ON_CANCELED,
- ON_FAILED,
- ON_SUCCEEDED,
- }
-
- public enum FailureType {
- NONE,
- CANCEL_SYNC,
- CANCEL_ASYNC,
- // Same as above, but continues to advance the stream after posting
- // the cancellation task.
- CANCEL_ASYNC_WITHOUT_PAUSE,
- THROW_SYNC
- }
-
- private boolean isTerminalCallback(ResponseStep step) {
- switch (step) {
- case ON_SUCCEEDED:
- case ON_CANCELED:
- case ON_FAILED:
- return true;
- default:
- return false;
- }
- }
-
- public TestBidirectionalStreamCallback() {
- mUseDirectExecutor = false;
- mDirectExecutor = null;
- }
-
- public TestBidirectionalStreamCallback(boolean useDirectExecutor) {
- mUseDirectExecutor = useDirectExecutor;
- mDirectExecutor = new DirectExecutor();
- }
-
- public void setAutoAdvance(boolean autoAdvance) {
- mAutoAdvance = autoAdvance;
- }
-
- public void setFailure(FailureType failureType, ResponseStep failureStep) {
- mFailureStep = failureStep;
- mFailureType = failureType;
- }
-
- public boolean blockForDone() {
- return mDone.block(TIMEOUT_MS);
- }
-
- /**
- * Waits for a terminal callback to complete execution before failing if the callback is not the
- * expected one
- *
- * @param expectedStep the expected callback step
- */
- public void expectCallback(ResponseStep expectedStep) {
- if (isTerminalCallback(expectedStep)) {
- assertTrue(String.format(
- "Request timed out. Expected %s callback. Current callback is %s",
- expectedStep, mResponseStep),
- blockForDone());
- }
- assertSame(expectedStep, mResponseStep);
- }
-
- /**
- * Waits for a terminal callback to complete execution before skipping the test if the callback
- * is not the expected one
- *
- * @param expectedStep the expected callback step
- */
- public void assumeCallback(ResponseStep expectedStep) {
- if (isTerminalCallback(expectedStep)) {
- assumeTrue(
- String.format(
- "Request timed out. Expected %s callback. Current callback is %s",
- expectedStep, mResponseStep),
- blockForDone());
- }
- assumeThat(expectedStep, equalTo(mResponseStep));
- }
-
- public void waitForNextReadStep() {
- mReadStepBlock.block();
- mReadStepBlock.close();
- }
-
- public void waitForNextWriteStep() {
- mWriteStepBlock.block();
- mWriteStepBlock.close();
- }
-
- public Executor getExecutor() {
- if (mUseDirectExecutor) {
- return mDirectExecutor;
- }
- return mExecutorService;
- }
-
- public void shutdownExecutor() {
- if (mUseDirectExecutor) {
- throw new UnsupportedOperationException("DirectExecutor doesn't support shutdown");
- }
- mExecutorService.shutdown();
- }
-
- public void addWriteData(byte[] data) {
- addWriteData(data, true);
- }
-
- public void addWriteData(byte[] data, boolean flush) {
- ByteBuffer writeBuffer = ByteBuffer.allocateDirect(data.length);
- writeBuffer.put(data);
- writeBuffer.flip();
- mWriteBuffers.add(new WriteBuffer(writeBuffer, flush));
- mWriteBuffersToBeAcked.add(new WriteBuffer(writeBuffer, flush));
- }
-
- @Override
- public void onStreamReady(BidirectionalStream stream) {
- checkOnValidThread();
- assertFalse(stream.isDone());
- assertEquals(ResponseStep.NOTHING, mResponseStep);
- assertNull(mError);
- mResponseStep = ResponseStep.ON_STREAM_READY;
- if (maybeThrowCancelOrPause(stream, mWriteStepBlock)) {
- return;
- }
- startNextWrite(stream);
- }
-
- @Override
- public void onResponseHeadersReceived(BidirectionalStream stream, UrlResponseInfo info) {
- checkOnValidThread();
- assertFalse(stream.isDone());
- assertTrue(
- mResponseStep == ResponseStep.NOTHING
- || mResponseStep == ResponseStep.ON_STREAM_READY
- || mResponseStep == ResponseStep.ON_WRITE_COMPLETED);
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_RESPONSE_STARTED;
- mResponseInfo = info;
- if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
- return;
- }
- startNextRead(stream);
- }
-
- @Override
- public void onReadCompleted(
- BidirectionalStream stream,
- UrlResponseInfo info,
- ByteBuffer byteBuffer,
- boolean endOfStream) {
- checkOnValidThread();
- assertFalse(stream.isDone());
- assertTrue(
- mResponseStep == ResponseStep.ON_RESPONSE_STARTED
- || mResponseStep == ResponseStep.ON_READ_COMPLETED
- || mResponseStep == ResponseStep.ON_WRITE_COMPLETED
- || mResponseStep == ResponseStep.ON_TRAILERS);
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_READ_COMPLETED;
- mResponseInfo = info;
-
- final int bytesRead = byteBuffer.position() - mBufferPositionBeforeRead;
- mHttpResponseDataLength += bytesRead;
- final byte[] lastDataReceivedAsBytes = new byte[bytesRead];
- // Rewind byteBuffer.position() to pre-read() position.
- byteBuffer.position(mBufferPositionBeforeRead);
- // This restores byteBuffer.position() to its value on entrance to
- // this function.
- byteBuffer.get(lastDataReceivedAsBytes);
-
- mResponseAsString += new String(lastDataReceivedAsBytes);
-
- if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
- return;
- }
- // Do not read if EOF has been reached.
- if (!endOfStream) {
- startNextRead(stream);
- }
- }
-
- @Override
- public void onWriteCompleted(
- BidirectionalStream stream,
- UrlResponseInfo info,
- ByteBuffer buffer,
- boolean endOfStream) {
- checkOnValidThread();
- assertFalse(stream.isDone());
- assertNull(mError);
- mResponseStep = ResponseStep.ON_WRITE_COMPLETED;
- mResponseInfo = info;
- if (!mWriteBuffersToBeAcked.isEmpty()) {
- assertEquals(buffer, mWriteBuffersToBeAcked.get(0).mBuffer);
- mWriteBuffersToBeAcked.remove(0);
- }
- if (maybeThrowCancelOrPause(stream, mWriteStepBlock)) {
- return;
- }
- startNextWrite(stream);
- }
-
- @Override
- public void onResponseTrailersReceived(
- BidirectionalStream stream,
- UrlResponseInfo info,
- HeaderBlock trailers) {
- checkOnValidThread();
- assertFalse(stream.isDone());
- assertNull(mError);
- mResponseStep = ResponseStep.ON_TRAILERS;
- mResponseInfo = info;
- mTrailers = trailers;
- if (maybeThrowCancelOrPause(stream, mReadStepBlock)) {
- return;
- }
- }
-
- @Override
- public void onSucceeded(BidirectionalStream stream, UrlResponseInfo info) {
- checkOnValidThread();
- assertTrue(stream.isDone());
- assertTrue(
- mResponseStep == ResponseStep.ON_RESPONSE_STARTED
- || mResponseStep == ResponseStep.ON_READ_COMPLETED
- || mResponseStep == ResponseStep.ON_WRITE_COMPLETED
- || mResponseStep == ResponseStep.ON_TRAILERS);
- assertFalse(mOnErrorCalled);
- assertFalse(mOnCanceledCalled);
- assertNull(mError);
- assertEquals(0, mWriteBuffers.size());
- assertEquals(0, mWriteBuffersToBeAcked.size());
-
- mResponseStep = ResponseStep.ON_SUCCEEDED;
- mResponseInfo = info;
- openDone();
- maybeThrowCancelOrPause(stream, mReadStepBlock);
- }
-
- @Override
- public void onFailed(BidirectionalStream stream, UrlResponseInfo info, HttpException error) {
- checkOnValidThread();
- assertTrue(stream.isDone());
- // Shouldn't happen after success.
- assertTrue(mResponseStep != ResponseStep.ON_SUCCEEDED);
- // Should happen at most once for a single stream.
- assertFalse(mOnErrorCalled);
- assertFalse(mOnCanceledCalled);
- assertNull(mError);
- mResponseStep = ResponseStep.ON_FAILED;
- mResponseInfo = info;
-
- mOnErrorCalled = true;
- mError = error;
- openDone();
- maybeThrowCancelOrPause(stream, mReadStepBlock);
- }
-
- @Override
- public void onCanceled(BidirectionalStream stream, UrlResponseInfo info) {
- checkOnValidThread();
- assertTrue(stream.isDone());
- // Should happen at most once for a single stream.
- assertFalse(mOnCanceledCalled);
- assertFalse(mOnErrorCalled);
- assertNull(mError);
- mResponseStep = ResponseStep.ON_CANCELED;
- mResponseInfo = info;
-
- mOnCanceledCalled = true;
- openDone();
- maybeThrowCancelOrPause(stream, mReadStepBlock);
- }
-
- public void startNextRead(BidirectionalStream stream) {
- startNextRead(stream, ByteBuffer.allocateDirect(READ_BUFFER_SIZE));
- }
-
- public void startNextRead(BidirectionalStream stream, ByteBuffer buffer) {
- mBufferPositionBeforeRead = buffer.position();
- stream.read(buffer);
- }
-
- public void startNextWrite(BidirectionalStream stream) {
- if (!mWriteBuffers.isEmpty()) {
- Iterator<WriteBuffer> iterator = mWriteBuffers.iterator();
- while (iterator.hasNext()) {
- WriteBuffer b = iterator.next();
- stream.write(b.mBuffer, !iterator.hasNext());
- iterator.remove();
- if (b.mFlush) {
- stream.flush();
- break;
- }
- }
- }
- }
-
- public boolean isDone() {
- // It's not mentioned by the Android docs, but block(0) seems to block
- // indefinitely, so have to block for one millisecond to get state
- // without blocking.
- return mDone.block(1);
- }
-
- /** Returns the number of pending Writes. */
- public int numPendingWrites() {
- return mWriteBuffers.size();
- }
-
- protected void openDone() {
- mDone.open();
- }
-
- /** Returns {@code false} if the callback should continue to advance the stream. */
- private boolean maybeThrowCancelOrPause(
- final BidirectionalStream stream, ConditionVariable stepBlock) {
- if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) {
- if (!mAutoAdvance) {
- stepBlock.open();
- return true;
- }
- return false;
- }
-
- if (mFailureType == FailureType.THROW_SYNC) {
- throw new IllegalStateException("Callback Exception.");
- }
- Runnable task =
- new Runnable() {
- @Override
- public void run() {
- stream.cancel();
- }
- };
- if (mFailureType == FailureType.CANCEL_ASYNC
- || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) {
- getExecutor().execute(task);
- } else {
- task.run();
- }
- return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE;
- }
-
- /** Checks whether callback methods are invoked on the correct thread. */
- private void checkOnValidThread() {
- if (!mUseDirectExecutor) {
- assertEquals(mExecutorThread, Thread.currentThread());
- }
- }
-}
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
deleted file mode 100644
index 3a4486f..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestStatusListener.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.http.cts.util
-
-import android.net.http.UrlRequest.StatusListener
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-import org.junit.Assert.assertSame
-
-private const val TIMEOUT_MS = 12000L
-
-/** Test status listener for requests */
-class TestStatusListener : StatusListener {
- private val statusFuture = CompletableFuture<Int>()
-
- override fun onStatus(status: Int) {
- statusFuture.complete(status)
- }
-
- /** Fails if the expected status is not the returned status */
- fun expectStatus(expected: Int) {
- assertSame(expected, statusFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS))
- }
-}
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
deleted file mode 100644
index 28443b7..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUrlRequestCallback.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.http.cts.util;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.core.AnyOf.anyOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeThat;
-import static org.junit.Assume.assumeTrue;
-
-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 java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Callback that tracks information from different callbacks and has a
- * method to block thread until the request completes on another thread.
- * Allows us to cancel, block request or throw an exception from an arbitrary step.
- */
-public class TestUrlRequestCallback implements UrlRequest.Callback {
- private static final int TIMEOUT_MS = 12_000;
- public ArrayList<UrlResponseInfo> mRedirectResponseInfoList = new ArrayList<>();
- public ArrayList<String> mRedirectUrlList = new ArrayList<>();
- public UrlResponseInfo mResponseInfo;
- public HttpException mError;
-
- public ResponseStep mResponseStep = ResponseStep.NOTHING;
-
- public int mRedirectCount;
- public boolean mOnErrorCalled;
- public boolean mOnCanceledCalled;
-
- public int mHttpResponseDataLength;
- public String mResponseAsString = "";
-
- public int mReadBufferSize = 32 * 1024;
-
- // When false, the consumer is responsible for all calls into the request
- // that advance it.
- private boolean mAutoAdvance = true;
- // Whether an exception is thrown by maybeThrowCancelOrPause().
- private boolean mCallbackExceptionThrown;
-
- // Whether to permit calls on the network thread.
- private boolean mAllowDirectExecutor;
-
- // Whether to stop the executor thread after reaching a terminal method.
- // Terminal methods are (onSucceeded, onFailed or onCancelled)
- private boolean mBlockOnTerminalState;
-
- // Conditionally fail on certain steps.
- private FailureType mFailureType = FailureType.NONE;
- private ResponseStep mFailureStep = ResponseStep.NOTHING;
-
- // Signals when request is done either successfully or not.
- private final ConditionVariable mDone = new ConditionVariable();
-
- // Hangs the calling thread until a terminal method has started executing.
- private final ConditionVariable mWaitForTerminalToStart = new ConditionVariable();
-
- // Signaled on each step when mAutoAdvance is false.
- private final ConditionVariable mStepBlock = new ConditionVariable();
-
- // Executor Service for Http callbacks.
- private final ExecutorService mExecutorService;
- private Thread mExecutorThread;
-
- // position() of ByteBuffer prior to read() call.
- private int mBufferPositionBeforeRead;
-
- private static class ExecutorThreadFactory implements ThreadFactory {
- @Override
- public Thread newThread(final Runnable r) {
- return new Thread(new Runnable() {
- @Override
- public void run() {
- StrictMode.ThreadPolicy threadPolicy = StrictMode.getThreadPolicy();
- try {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectNetwork()
- .penaltyLog()
- .penaltyDeath()
- .build());
- r.run();
- } finally {
- StrictMode.setThreadPolicy(threadPolicy);
- }
- }
- });
- }
- }
-
- public enum ResponseStep {
- NOTHING,
- ON_RECEIVED_REDIRECT,
- ON_RESPONSE_STARTED,
- ON_READ_COMPLETED,
- ON_SUCCEEDED,
- ON_FAILED,
- ON_CANCELED,
- }
-
- public enum FailureType {
- NONE,
- CANCEL_SYNC,
- CANCEL_ASYNC,
- // Same as above, but continues to advance the request after posting
- // the cancellation task.
- CANCEL_ASYNC_WITHOUT_PAUSE,
- THROW_SYNC
- }
-
- private static void assertContains(String expectedSubstring, String actualString) {
- assertNotNull(actualString);
- assertTrue("String [" + actualString + "] doesn't contain substring [" + expectedSubstring
- + "]", actualString.contains(expectedSubstring));
-
- }
-
- /**
- * Set {@code mExecutorThread}.
- */
- private void fillInExecutorThread() {
- mExecutorService.execute(new Runnable() {
- @Override
- public void run() {
- mExecutorThread = Thread.currentThread();
- }
- });
- }
-
- private boolean isTerminalCallback(ResponseStep step) {
- switch (step) {
- case ON_SUCCEEDED:
- case ON_CANCELED:
- case ON_FAILED:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Create a {@link TestUrlRequestCallback} with a new single-threaded executor.
- */
- public TestUrlRequestCallback() {
- this(Executors.newSingleThreadExecutor(new ExecutorThreadFactory()));
- }
-
- /**
- * Create a {@link TestUrlRequestCallback} using a custom single-threaded executor.
- */
- public TestUrlRequestCallback(ExecutorService executorService) {
- mExecutorService = executorService;
- fillInExecutorThread();
- }
-
- /**
- * This blocks the callback executor thread once it has reached a final state callback.
- * In order to continue execution, this method must be called again and providing {@code false}
- * to continue execution.
- *
- * @param blockOnTerminalState the state to set for the executor thread
- */
- public void setBlockOnTerminalState(boolean blockOnTerminalState) {
- mBlockOnTerminalState = blockOnTerminalState;
- if (!blockOnTerminalState) {
- mDone.open();
- }
- }
-
- public void setAutoAdvance(boolean autoAdvance) {
- mAutoAdvance = autoAdvance;
- }
-
- public void setAllowDirectExecutor(boolean allowed) {
- mAllowDirectExecutor = allowed;
- }
-
- public void setFailure(FailureType failureType, ResponseStep failureStep) {
- mFailureStep = failureStep;
- mFailureType = failureType;
- }
-
- /**
- * Blocks the calling thread till callback execution is done
- *
- * @return true if the condition was opened, false if the call returns because of the timeout.
- */
- public boolean blockForDone() {
- return mDone.block(TIMEOUT_MS);
- }
-
- /**
- * Waits for a terminal callback to complete execution before failing if the callback
- * is not the expected one
- *
- * @param expectedStep the expected callback step
- */
- public void expectCallback(ResponseStep expectedStep) {
- if (isTerminalCallback(expectedStep)) {
- assertTrue("Did not receive terminal callback before timeout", blockForDone());
- }
- assertSame(expectedStep, mResponseStep);
- }
-
- /**
- * Waits for a terminal callback to complete execution before skipping the test if the
- * callback is not the expected one
- *
- * @param expectedStep the expected callback step
- */
- public void assumeCallback(ResponseStep expectedStep) {
- if (isTerminalCallback(expectedStep)) {
- assumeTrue("Did not receive terminal callback before timeout", blockForDone());
- }
- assumeThat(expectedStep, equalTo(mResponseStep));
- }
-
- /**
- * Blocks the calling thread until one of the final states has been called.
- * This is called before the callback has finished executed.
- */
- public void waitForTerminalToStart() {
- mWaitForTerminalToStart.block();
- }
-
- public void waitForNextStep() {
- mStepBlock.block();
- mStepBlock.close();
- }
-
- public ExecutorService getExecutor() {
- return mExecutorService;
- }
-
- public void shutdownExecutor() {
- mExecutorService.shutdown();
- }
-
- /**
- * Shuts down the ExecutorService and waits until it executes all posted
- * tasks.
- */
- public void shutdownExecutorAndWait() {
- mExecutorService.shutdown();
- try {
- // Termination shouldn't take long. Use 1 min which should be more than enough.
- mExecutorService.awaitTermination(1, TimeUnit.MINUTES);
- } catch (InterruptedException e) {
- fail("ExecutorService is interrupted while waiting for termination");
- }
- assertTrue(mExecutorService.isTerminated());
- }
-
- @Override
- public void onRedirectReceived(
- UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
- checkExecutorThread();
- assertFalse(request.isDone());
- assertThat(mResponseStep, anyOf(
- equalTo(ResponseStep.NOTHING),
- equalTo(ResponseStep.ON_RECEIVED_REDIRECT)));
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_RECEIVED_REDIRECT;
- mRedirectUrlList.add(newLocationUrl);
- mRedirectResponseInfoList.add(info);
- ++mRedirectCount;
- if (maybeThrowCancelOrPause(request)) {
- return;
- }
- request.followRedirect();
- }
-
- @Override
- public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
- checkExecutorThread();
- assertFalse(request.isDone());
- assertThat(mResponseStep, anyOf(
- equalTo(ResponseStep.NOTHING),
- equalTo(ResponseStep.ON_RECEIVED_REDIRECT)));
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_RESPONSE_STARTED;
- mResponseInfo = info;
- if (maybeThrowCancelOrPause(request)) {
- return;
- }
- startNextRead(request);
- }
-
- @Override
- public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
- checkExecutorThread();
- assertFalse(request.isDone());
- assertThat(mResponseStep, anyOf(
- equalTo(ResponseStep.ON_RESPONSE_STARTED),
- equalTo(ResponseStep.ON_READ_COMPLETED)));
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_READ_COMPLETED;
-
- final byte[] lastDataReceivedAsBytes;
- final int bytesRead = byteBuffer.position() - mBufferPositionBeforeRead;
- mHttpResponseDataLength += bytesRead;
- lastDataReceivedAsBytes = new byte[bytesRead];
- // Rewind |byteBuffer.position()| to pre-read() position.
- byteBuffer.position(mBufferPositionBeforeRead);
- // This restores |byteBuffer.position()| to its value on entrance to
- // this function.
- byteBuffer.get(lastDataReceivedAsBytes);
- mResponseAsString += new String(lastDataReceivedAsBytes);
-
- if (maybeThrowCancelOrPause(request)) {
- return;
- }
- startNextRead(request);
- }
-
- @Override
- public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
- checkExecutorThread();
- assertTrue(request.isDone());
- assertThat(mResponseStep, anyOf(
- equalTo(ResponseStep.ON_RESPONSE_STARTED),
- equalTo(ResponseStep.ON_READ_COMPLETED)));
- assertFalse(mOnErrorCalled);
- assertFalse(mOnCanceledCalled);
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_SUCCEEDED;
- mResponseInfo = info;
- mWaitForTerminalToStart.open();
- if (mBlockOnTerminalState) mDone.block();
- openDone();
- maybeThrowCancelOrPause(request);
- }
-
- @Override
- public void onFailed(UrlRequest request, UrlResponseInfo info, 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) {
- mAllowDirectExecutor = true;
- }
- checkExecutorThread();
- assertTrue(request.isDone());
- // Shouldn't happen after success.
- assertNotEquals(ResponseStep.ON_SUCCEEDED, mResponseStep);
- // Should happen at most once for a single request.
- assertFalse(mOnErrorCalled);
- assertFalse(mOnCanceledCalled);
- assertNull(mError);
- if (mCallbackExceptionThrown) {
- assertTrue(error instanceof CallbackException);
- assertContains("Exception received from UrlRequest.Callback", error.getMessage());
- assertNotNull(error.getCause());
- assertTrue(error.getCause() instanceof IllegalStateException);
- assertContains("Listener Exception.", error.getCause().getMessage());
- }
-
- mResponseStep = ResponseStep.ON_FAILED;
- mOnErrorCalled = true;
- mError = error;
- mWaitForTerminalToStart.open();
- if (mBlockOnTerminalState) mDone.block();
- openDone();
- maybeThrowCancelOrPause(request);
- }
-
- @Override
- public void onCanceled(UrlRequest request, UrlResponseInfo info) {
- checkExecutorThread();
- assertTrue(request.isDone());
- // Should happen at most once for a single request.
- assertFalse(mOnCanceledCalled);
- assertFalse(mOnErrorCalled);
- assertNull(mError);
-
- mResponseStep = ResponseStep.ON_CANCELED;
- mOnCanceledCalled = true;
- mWaitForTerminalToStart.open();
- if (mBlockOnTerminalState) mDone.block();
- openDone();
- maybeThrowCancelOrPause(request);
- }
-
- public void startNextRead(UrlRequest request) {
- startNextRead(request, ByteBuffer.allocateDirect(mReadBufferSize));
- }
-
- public void startNextRead(UrlRequest request, ByteBuffer buffer) {
- mBufferPositionBeforeRead = buffer.position();
- request.read(buffer);
- }
-
- public boolean isDone() {
- // It's not mentioned by the Android docs, but block(0) seems to block
- // indefinitely, so have to block for one millisecond to get state
- // without blocking.
- return mDone.block(1);
- }
-
- protected void openDone() {
- mDone.open();
- }
-
- private void checkExecutorThread() {
- if (!mAllowDirectExecutor) {
- assertEquals(mExecutorThread, Thread.currentThread());
- }
- }
-
- /**
- * Returns {@code false} if the listener should continue to advance the
- * request.
- */
- private boolean maybeThrowCancelOrPause(final UrlRequest request) {
- checkExecutorThread();
- if (mResponseStep != mFailureStep || mFailureType == FailureType.NONE) {
- if (!mAutoAdvance) {
- mStepBlock.open();
- return true;
- }
- return false;
- }
-
- if (mFailureType == FailureType.THROW_SYNC) {
- assertFalse(mCallbackExceptionThrown);
- mCallbackExceptionThrown = true;
- throw new IllegalStateException("Listener Exception.");
- }
- Runnable task = new Runnable() {
- @Override
- public void run() {
- request.cancel();
- }
- };
- if (mFailureType == FailureType.CANCEL_ASYNC
- || mFailureType == FailureType.CANCEL_ASYNC_WITHOUT_PAUSE) {
- getExecutor().execute(task);
- } else {
- task.run();
- }
- return mFailureType != FailureType.CANCEL_ASYNC_WITHOUT_PAUSE;
- }
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt b/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt
deleted file mode 100644
index 7fc005a..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUtils.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.hamcrest.Matchers.equalTo
-import org.junit.Assert.assertEquals
-import org.junit.Assume.assumeNotNull
-import org.junit.Assume.assumeThat
-
-fun skipIfNoInternetConnection(context: Context) {
- val connectivityManager = context.getSystemService(ConnectivityManager::class.java)
- assumeNotNull(
- "This test requires a working Internet connection", connectivityManager!!.activeNetwork
- )
-}
-
-fun assertOKStatusCode(info: UrlResponseInfo) {
- assertEquals("Status code must be 200 OK", 200, info.httpStatusCode)
-}
-
-fun assumeOKStatusCode(info: UrlResponseInfo) {
- assumeThat("Status code must be 200 OK", info.httpStatusCode, equalTo(200))
-}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
deleted file mode 100644
index 3b90fa0..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.net.http.UploadDataProvider;
-import android.net.http.UploadDataSink;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
- * {@code android.net.http.apihelpers.UploadDataProviders} which is not an exposed API.
- */
-public final class UploadDataProviders {
- /**
- * Uploads an entire file.
- *
- * @param file The file to upload
- * @return A new UploadDataProvider for the given file
- */
- public static UploadDataProvider create(final File file) {
- return new FileUploadProvider(() -> new FileInputStream(file).getChannel());
- }
-
- /**
- * Uploads an entire file, closing the descriptor when it is no longer needed.
- *
- * @param fd The file descriptor to upload
- * @throws IllegalArgumentException if {@code fd} is not a file.
- * @return A new UploadDataProvider for the given file descriptor
- */
- public static UploadDataProvider create(final ParcelFileDescriptor fd) {
- return new FileUploadProvider(() -> {
- if (fd.getStatSize() != -1) {
- return new ParcelFileDescriptor.AutoCloseInputStream(fd).getChannel();
- } else {
- fd.close();
- throw new IllegalArgumentException("Not a file: " + fd);
- }
- });
- }
-
- /**
- * Uploads a ByteBuffer, from the current {@code buffer.position()} to {@code buffer.limit()}
- *
- * @param buffer The data to upload
- * @return A new UploadDataProvider for the given buffer
- */
- public static UploadDataProvider create(ByteBuffer buffer) {
- return new ByteBufferUploadProvider(buffer.slice());
- }
-
- /**
- * Uploads {@code length} bytes from {@code data}, starting from {@code offset}
- *
- * @param data Array containing data to upload
- * @param offset Offset within data to start with
- * @param length Number of bytes to upload
- * @return A new UploadDataProvider for the given data
- */
- public static UploadDataProvider create(byte[] data, int offset, int length) {
- return new ByteBufferUploadProvider(ByteBuffer.wrap(data, offset, length).slice());
- }
-
- /**
- * Uploads the contents of {@code data}
- *
- * @param data Array containing data to upload
- * @return A new UploadDataProvider for the given data
- */
- public static UploadDataProvider create(byte[] data) {
- return create(data, 0, data.length);
- }
-
- /**
- * Uploads the UTF-8 representation of {@code data}
- *
- * @param data String containing data to upload
- * @return A new UploadDataProvider for the given data
- */
- public static UploadDataProvider create(String data) {
- return create(data.getBytes(StandardCharsets.UTF_8));
- }
-
- private interface FileChannelProvider {
- FileChannel getChannel() throws IOException;
- }
-
- private static final class FileUploadProvider extends UploadDataProvider {
- private volatile FileChannel mChannel;
- private final FileChannelProvider mProvider;
- /** Guards initialization of {@code mChannel} */
- private final Object mLock = new Object();
-
- private FileUploadProvider(FileChannelProvider provider) {
- this.mProvider = provider;
- }
-
- @Override
- public long getLength() throws IOException {
- return getChannel().size();
- }
-
- @Override
- public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) throws IOException {
- if (!byteBuffer.hasRemaining()) {
- throw new IllegalStateException("Cronet passed a buffer with no bytes remaining");
- }
- FileChannel channel = getChannel();
- int bytesRead = 0;
- while (bytesRead == 0) {
- int read = channel.read(byteBuffer);
- if (read == -1) {
- break;
- } else {
- bytesRead += read;
- }
- }
- uploadDataSink.onReadSucceeded(false);
- }
-
- @Override
- public void rewind(UploadDataSink uploadDataSink) throws IOException {
- getChannel().position(0);
- uploadDataSink.onRewindSucceeded();
- }
-
- /**
- * Lazily initializes the channel so that a blocking operation isn't performed
- * on a non-executor thread.
- */
- private FileChannel getChannel() throws IOException {
- if (mChannel == null) {
- synchronized (mLock) {
- if (mChannel == null) {
- mChannel = mProvider.getChannel();
- }
- }
- }
- return mChannel;
- }
-
- @Override
- public void close() throws IOException {
- FileChannel channel = mChannel;
- if (channel != null) {
- channel.close();
- }
- }
- }
-
- private static final class ByteBufferUploadProvider extends UploadDataProvider {
- private final ByteBuffer mUploadBuffer;
-
- private ByteBufferUploadProvider(ByteBuffer uploadBuffer) {
- this.mUploadBuffer = uploadBuffer;
- }
-
- @Override
- public long getLength() {
- return mUploadBuffer.limit();
- }
-
- @Override
- public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
- if (!byteBuffer.hasRemaining()) {
- throw new IllegalStateException("Cronet passed a buffer with no bytes remaining");
- }
- if (byteBuffer.remaining() >= mUploadBuffer.remaining()) {
- byteBuffer.put(mUploadBuffer);
- } else {
- int oldLimit = mUploadBuffer.limit();
- mUploadBuffer.limit(mUploadBuffer.position() + byteBuffer.remaining());
- byteBuffer.put(mUploadBuffer);
- mUploadBuffer.limit(oldLimit);
- }
- uploadDataSink.onReadSucceeded(false);
- }
-
- @Override
- public void rewind(UploadDataSink uploadDataSink) {
- mUploadBuffer.position(0);
- uploadDataSink.onRewindSucceeded();
- }
- }
-
- // Prevent instantiation
- private UploadDataProviders() {}
-}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d8d4c21..375397b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,4 +1,152 @@
{
+ "captiveportal-networkstack-resolve-tethering-mainline-presubmit": [
+ {
+ "name": "CtsNetTestCasesLatestSdk",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ },
+ {
+ "name": "CtsNetTestCasesMaxTargetSdk30",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ },
+ {
+ "name": "CtsNetTestCasesMaxTargetSdk31",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ },
+ {
+ "name": "CtsNetTestCasesMaxTargetSdk33",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ }
+ ]
+ },
+ {
+ "name": "bpf_existence_test"
+ },
+ {
+ "name": "connectivity_native_test"
+ },
+ {
+ "name": "netd_updatable_unit_test"
+ },
+ {
+ "name": "ConnectivityCoverageTests",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ]
+ },
+ {
+ "name": "libnetworkstats_test"
+ },
+ {
+ "name": "NetHttpCoverageTests",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ // These sometimes take longer than 1 min which is the presubmit timeout
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTetheringTestLatestSdk",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.NetworkStackModuleTest"
+ }
+ ]
+ }
+ ],
+ "captiveportal-networkstack-mainline-presubmit": [
+ // Test with APK modules only, in cases where APEX is not supported, or the other modules
+ // were simply not updated
+ {
+ "name": "CtsNetTestCasesLatestSdk",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.DnsResolverModuleTest"
+ }
+ ]
+ }
+ ],
+ "tethering-mainline-presubmit": [
+ // Test with connectivity/tethering module only, to catch integration issues with older versions
+ // of other modules. "new tethering + old NetworkStack" is not a configuration that should
+ // really exist in the field, but there is no strong guarantee, and it is required by MTS
+ // testing for module qualification, where modules are tested independently.
+ {
+ "name": "CtsNetTestCasesLatestSdk",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipMainlinePresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.DnsResolverModuleTest"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.NetworkStackModuleTest"
+ }
+ ]
+ }
+ ],
"presubmit": [
{
"name": "ConnectivityCoverageTests",
@@ -104,19 +252,6 @@
},
{
"name": "FrameworksNetIntegrationTests"
- },
- // Runs both NetHttpTests and CtsNetHttpTestCases
- {
- "name": "NetHttpCoverageTests",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- // These sometimes take longer than 1 min which is the presubmit timeout
- "exclude-annotation": "androidx.test.filters.LargeTest"
- }
- ]
}
],
"postsubmit": [
@@ -144,9 +279,6 @@
},
{
"name": "FrameworksNetTests"
- },
- {
- "name": "NetHttpCoverageTests"
}
],
"mainline-presubmit": [
@@ -273,18 +405,6 @@
"name": "libnetworkstats_test[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
},
{
- "name": "NetHttpCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- // These sometimes take longer than 1 min which is the presubmit timeout
- "exclude-annotation": "androidx.test.filters.LargeTest"
- }
- ]
- },
- {
"name": "CtsTetheringTestLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
@@ -332,6 +452,9 @@
"path": "packages/modules/CaptivePortalLogin"
},
{
+ "path": "external/cronet"
+ },
+ {
"path": "vendor/xts/gts-tests/hostsidetests/networkstack"
}
]
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 56537d9..02ef8b7 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -100,23 +100,26 @@
public static final long ENABLE_MATCH_LOCAL_NETWORK = 319212206L;
/**
- * On Android {@link android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM} or higher releases, when
- * apps targeting Android {@link android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM} or higher
- * that do not have the {@link android.Manifest.permission#INTERNET} permission call
- * {@link android.net.ConnectivityManager#getActiveNetworkInfo()}, the state of the returned
- * {@link android.net.NetworkInfo} object will always be
- * {@link android.net.NetworkInfo.DetailedState#BLOCKED}. This is because apps without the
- * permission cannot access any network.
+ * On Android {@link android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM} or higher releases,
+ * network access from apps targeting Android 36 or higher that do not have the
+ * {@link android.Manifest.permission#INTERNET} permission is considered blocked.
+ * This results in API behaviors change for apps without
+ * {@link android.Manifest.permission#INTERNET} permission.
+ * {@link android.net.NetworkInfo} returned from {@link android.net.ConnectivityManager} APIs
+ * always has {@link android.net.NetworkInfo.DetailedState#BLOCKED}.
+ * {@link android.net.ConnectivityManager#getActiveNetwork()} always returns null.
+ * {@link android.net.ConnectivityManager.NetworkCallback#onBlockedStatusChanged()} is always
+ * called with blocked=true.
* <p>
* For backwards compatibility, apps running on older releases, or targeting older SDK levels,
- * will instead receive objects with the network's current state,
- * such as {@link android.net.NetworkInfo.DetailedState#CONNECTED}.
+ * network access from apps without {@link android.Manifest.permission#INTERNET} permission is
+ * considered not blocked even though apps cannot access any networks.
*
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final long NETWORKINFO_WITHOUT_INTERNET_BLOCKED = 333340911L;
+ @EnabledAfter(targetSdkVersion = 35) // TODO: change to VANILLA_ICE_CREAM.
+ public static final long NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION = 333340911L;
private ConnectivityCompatChanges() {
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index 92f1953..b8689d6 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -108,11 +108,7 @@
PermissionUtils.enforceRestrictedNetworkPermission(mContext, TAG);
}
- // This causes thread-unsafe access on mIpConfigurations which might
- // race with calls to EthernetManager#updateConfiguration().
- // EthernetManager#getConfiguration() has been marked as
- // @UnsupportedAppUsage since Android R.
- return mTracker.getIpConfiguration(iface);
+ return new IpConfiguration(mTracker.getIpConfiguration(iface));
}
/**
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index a60592f..71f289e 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -31,6 +31,8 @@
import android.net.ITetheredInterfaceCallback;
import android.net.InterfaceConfigurationParcel;
import android.net.IpConfiguration;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
@@ -109,7 +111,6 @@
/** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
new ConcurrentHashMap<>();
- /** Mapping between {iface name | mac address} -> {IpConfiguration} */
private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
new ConcurrentHashMap<>();
@@ -297,7 +298,7 @@
}
private IpConfiguration getIpConfigurationForCallback(String iface, int state) {
- return (state == EthernetManager.STATE_ABSENT) ? null : getIpConfiguration(iface);
+ return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface);
}
private void ensureRunningOnEthernetServiceThread() {
@@ -390,83 +391,8 @@
mHandler.post(() -> setInterfaceAdministrativeState(iface, enabled, cb));
}
- private @Nullable String getHwAddress(String iface) {
- if (getInterfaceRole(iface) == EthernetManager.ROLE_SERVER) {
- return mTetheringInterfaceHwAddr;
- }
-
- return mFactory.getHwAddress(iface);
- }
-
- /**
- * Get the IP configuration of the interface, or the default if the interface doesn't exist.
- * @param iface the name of the interface to retrieve.
- *
- * @return The IP configuration
- */
- public IpConfiguration getIpConfiguration(String iface) {
- return getIpConfiguration(iface, getHwAddress(iface));
- }
-
- private IpConfiguration getIpConfiguration(String iface, @Nullable String hwAddress) {
- // Look up Ip configuration first by ifname, then by MAC address.
- IpConfiguration ipConfig = mIpConfigurations.get(iface);
- if (ipConfig != null) {
- return ipConfig;
- }
-
- if (hwAddress == null) {
- // should never happen.
- Log.wtf(TAG, "No hardware address for interface " + iface);
- } else {
- ipConfig = mIpConfigurations.get(hwAddress);
- }
-
- if (ipConfig == null) {
- ipConfig = new IpConfiguration.Builder().build();
- }
-
- return ipConfig;
- }
-
- private NetworkCapabilities getNetworkCapabilities(String iface) {
- return getNetworkCapabilities(iface, getHwAddress(iface));
- }
-
- private NetworkCapabilities getNetworkCapabilities(String iface, @Nullable String hwAddress) {
- // Look up network capabilities first by ifname, then by MAC address.
- NetworkCapabilities networkCapabilities = mNetworkCapabilities.get(iface);
- if (networkCapabilities != null) {
- return networkCapabilities;
- }
-
- if (hwAddress == null) {
- // should never happen.
- Log.wtf(TAG, "No hardware address for interface " + iface);
- } else {
- networkCapabilities = mNetworkCapabilities.get(hwAddress);
- }
-
- if (networkCapabilities != null) {
- return networkCapabilities;
- }
-
- final NetworkCapabilities.Builder builder = createNetworkCapabilities(
- false /* clear default capabilities */, null, null)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-
- if (isValidTestInterface(iface)) {
- builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
- } else {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
-
- return builder.build();
+ IpConfiguration getIpConfiguration(String iface) {
+ return mIpConfigurations.get(iface);
}
@VisibleForTesting(visibility = PACKAGE)
@@ -507,8 +433,8 @@
* NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false.
*/
boolean isRestrictedInterface(String iface) {
- final NetworkCapabilities nc = getNetworkCapabilities(iface);
- return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ final NetworkCapabilities nc = mNetworkCapabilities.get(iface);
+ return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
@@ -697,9 +623,17 @@
return;
}
- final NetworkCapabilities nc = getNetworkCapabilities(iface, hwAddress);
- final IpConfiguration ipConfiguration = getIpConfiguration(iface, hwAddress);
+ NetworkCapabilities nc = mNetworkCapabilities.get(iface);
+ if (nc == null) {
+ // Try to resolve using mac address
+ nc = mNetworkCapabilities.get(hwAddress);
+ if (nc == null) {
+ final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP);
+ nc = createDefaultNetworkCapabilities(isTestIface);
+ }
+ }
+ IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
Log.d(TAG, "Tracking interface in client mode: " + iface);
mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
@@ -839,6 +773,25 @@
return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4));
}
+ private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) {
+ NetworkCapabilities.Builder builder = createNetworkCapabilities(
+ false /* clear default capabilities */, null, null)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ if (isTestIface) {
+ builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
+ } else {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ return builder.build();
+ }
+
/**
* Parses a static list of network capabilities
*
@@ -973,6 +926,15 @@
return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build();
}
+ private IpConfiguration getOrCreateIpConfiguration(String iface) {
+ IpConfiguration ret = mIpConfigurations.get(iface);
+ if (ret != null) return ret;
+ ret = new IpConfiguration();
+ ret.setIpAssignment(IpAssignment.DHCP);
+ ret.setProxySettings(ProxySettings.NONE);
+ return ret;
+ }
+
private boolean isValidEthernetInterface(String iface) {
return iface.matches(mIfaceMatch) || isValidTestInterface(iface);
}
@@ -1059,7 +1021,7 @@
pw.println("IP Configurations:");
pw.increaseIndent();
for (String iface : mIpConfigurations.keySet()) {
- pw.println(iface + ": " + getIpConfiguration(iface));
+ pw.println(iface + ": " + mIpConfigurations.get(iface));
}
pw.decreaseIndent();
pw.println();
@@ -1067,7 +1029,7 @@
pw.println("Network Capabilities:");
pw.increaseIndent();
for (String iface : mNetworkCapabilities.keySet()) {
- pw.println(iface + ": " + getNetworkCapabilities(iface));
+ pw.println(iface + ": " + mNetworkCapabilities.get(iface));
}
pw.decreaseIndent();
pw.println();
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 46bd9bc..39ea286 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -102,7 +102,7 @@
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION;
-import static android.net.connectivity.ConnectivityCompatChanges.NETWORKINFO_WITHOUT_INTERNET_BLOCKED;
+import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
import static android.system.OsConstants.ETH_P_ALL;
@@ -2229,6 +2229,11 @@
return nai;
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private boolean hasInternetPermission(final int uid) {
+ return (mBpfNetMaps.getNetPermForUid(uid) & PERMISSION_INTERNET) != 0;
+ }
+
/**
* Check if UID should be blocked from using the specified network.
*/
@@ -2243,8 +2248,13 @@
try {
final boolean metered = nc == null ? true : nc.isMetered();
if (mDeps.isAtLeastV()) {
- return mBpfNetMaps.isUidNetworkingBlocked(uid, metered)
- || (mBpfNetMaps.getNetPermForUid(uid) & PERMISSION_INTERNET) == 0;
+ final boolean hasInternetPermission = hasInternetPermission(uid);
+ final boolean blockedByUidRules = mBpfNetMaps.isUidNetworkingBlocked(uid, metered);
+ if (mDeps.isChangeEnabled(NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION, uid)) {
+ return blockedByUidRules || !hasInternetPermission;
+ } else {
+ return hasInternetPermission && blockedByUidRules;
+ }
} else {
return mPolicyManager.isUidNetworkingBlocked(uid, metered);
}
@@ -2325,12 +2335,7 @@
final int uid = mDeps.getCallingUid();
final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
if (nai == null) return null;
- // Ignore blocked state to keep the backward compatibility if the compat flag is
- // disabled and app does not have PERMISSION_INTERNET.
- final boolean ignoreBlocked = mDeps.isAtLeastV()
- && !mDeps.isChangeEnabled(NETWORKINFO_WITHOUT_INTERNET_BLOCKED, uid)
- && (mBpfNetMaps.getNetPermForUid(uid) & PERMISSION_INTERNET) == 0;
- final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, ignoreBlocked);
+ final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, false /* ignoreBlocked */);
maybeLogBlockedNetworkInfo(networkInfo, uid);
return networkInfo;
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 717c5a1..b6cb09b 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -152,7 +152,7 @@
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
import static android.net.Proxy.PROXY_CHANGE_ACTION;
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import static android.net.connectivity.ConnectivityCompatChanges.NETWORKINFO_WITHOUT_INTERNET_BLOCKED;
+import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_REMOVED;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -1948,7 +1948,8 @@
setAlwaysOnNetworks(false);
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
- mDeps.setChangeIdEnabled(true, NETWORKINFO_WITHOUT_INTERNET_BLOCKED, Process.myUid());
+ mDeps.setChangeIdEnabled(
+ true, NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION, Process.myUid());
doReturn(PERMISSION_INTERNET).when(mBpfNetMaps).getNetPermForUid(anyInt());
// Note : Please do not add any new instrumentation here. If you need new instrumentation,
// please add it in CSTest and use subclasses of CSTest instead of adding more
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSActiveNetworkInfoTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSActiveNetworkInfoTest.kt
index 1891a78..360a298 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSActiveNetworkInfoTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSActiveNetworkInfoTest.kt
@@ -24,7 +24,7 @@
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkInfo.DetailedState.BLOCKED
import android.net.NetworkInfo.DetailedState.CONNECTED
-import android.net.connectivity.ConnectivityCompatChanges.NETWORKINFO_WITHOUT_INTERNET_BLOCKED
+import android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -53,7 +53,7 @@
permissions: Int,
expectBlocked: Boolean
) {
- deps.setChangeIdEnabled(changeEnabled, NETWORKINFO_WITHOUT_INTERNET_BLOCKED)
+ deps.setChangeIdEnabled(changeEnabled, NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION)
doReturn(permissions).`when`(bpfNetMaps).getNetPermForUid(anyInt())
val agent = Agent(nc = nc())
@@ -66,6 +66,7 @@
} else {
assertEquals(CONNECTED, networkInfo.detailedState)
}
+ assertEquals(expectBlocked, cm.activeNetwork == null)
agent.disconnect()
}
@@ -82,8 +83,8 @@
permissions = PERMISSION_INTERNET,
expectBlocked = true
)
- // getActiveNetworkInfo does not return NetworkInfo with blocked state if the compat change
- // is disabled and the app does not have PERMISSION_INTERNET
+ // Network access is considered not blocked if the compat change is disabled and an app
+ // does not have PERMISSION_INTERNET
doTestGetActiveNetworkInfo(
changeEnabled = false,
permissions = PERMISSION_NONE,
diff --git a/thread/TEST_MAPPING b/thread/TEST_MAPPING
index ebbb9af..34d67bb 100644
--- a/thread/TEST_MAPPING
+++ b/thread/TEST_MAPPING
@@ -11,5 +11,8 @@
}
],
"postsubmit": [
+ {
+ "name": "ThreadNetworkMultiDeviceTests"
+ }
]
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 61bc5c1..0c200fd 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -122,6 +122,7 @@
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.Ipv6AddressInfo;
import com.android.server.thread.openthread.MeshcopTxtAttributes;
+import com.android.server.thread.openthread.OnMeshPrefixConfig;
import com.android.server.thread.openthread.OtDaemonState;
import libcore.util.HexEncoding;
@@ -1234,6 +1235,18 @@
}
}
+ private void handlePrefixChanged(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
+ checkOnHandlerThread();
+
+ mTunIfController.updatePrefixes(onMeshPrefixConfigList);
+
+ // The OT daemon can send link property updates before the networkAgent is
+ // registered
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendLinkProperties(mTunIfController.getLinkProperties());
+ }
+ }
+
private void sendLocalNetworkConfig() {
if (mNetworkAgent == null) {
return;
@@ -1358,7 +1371,6 @@
private void notifyThreadEnabledUpdated(IStateCallback callback, int enabledState) {
try {
callback.onThreadEnableStateChanged(enabledState);
- Log.i(TAG, "onThreadEnableStateChanged " + enabledState);
} catch (RemoteException ignored) {
// do nothing if the client is dead
}
@@ -1582,5 +1594,10 @@
public void onBackboneRouterStateChanged(BackboneRouterState state) {
mHandler.post(() -> handleMulticastForwardingChanged(state));
}
+
+ @Override
+ public void onPrefixChanged(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
+ mHandler.post(() -> handlePrefixChanged(onMeshPrefixConfigList));
+ }
}
}
diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java
index dec72b2..c3f6ace 100644
--- a/thread/service/java/com/android/server/thread/TunInterfaceController.java
+++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java
@@ -35,6 +35,7 @@
import com.android.net.module.util.netlink.NetlinkUtils;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.server.thread.openthread.Ipv6AddressInfo;
+import com.android.server.thread.openthread.OnMeshPrefixConfig;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -66,7 +67,8 @@
private static int sNetlinkSeqNo = 0;
private final MulticastSocket mMulticastSocket; // For join group and leave group
private NetworkInterface mNetworkInterface;
- private List<InetAddress> mMulticastAddresses = new ArrayList<>();
+ private final List<InetAddress> mMulticastAddresses = new ArrayList<>();
+ private final List<RouteInfo> mNetDataPrefixes = new ArrayList<>();
/** Creates a new {@link TunInterfaceController} instance for given interface. */
public TunInterfaceController(String interfaceName) {
@@ -243,13 +245,40 @@
mMulticastAddresses.addAll(newMulticastAddresses);
}
+ public void updatePrefixes(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
+ final List<RouteInfo> newNetDataPrefixes = new ArrayList<>();
+
+ for (OnMeshPrefixConfig onMeshPrefixConfig : onMeshPrefixConfigList) {
+ newNetDataPrefixes.add(getRouteForOnMeshPrefix(onMeshPrefixConfig));
+ }
+
+ final CompareResult<RouteInfo> prefixDiff =
+ new CompareResult<>(mNetDataPrefixes, newNetDataPrefixes);
+ for (RouteInfo routeRemoved : prefixDiff.removed) {
+ mLinkProperties.removeRoute(routeRemoved);
+ }
+ for (RouteInfo routeAdded : prefixDiff.added) {
+ mLinkProperties.addRoute(routeAdded);
+ }
+
+ mNetDataPrefixes.clear();
+ mNetDataPrefixes.addAll(newNetDataPrefixes);
+ }
+
private RouteInfo getRouteForAddress(LinkAddress linkAddress) {
- return new RouteInfo(
- new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()),
- null,
- mIfName,
- RouteInfo.RTN_UNICAST,
- MTU);
+ return getRouteForIpPrefix(
+ new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()));
+ }
+
+ private RouteInfo getRouteForOnMeshPrefix(OnMeshPrefixConfig onMeshPrefixConfig) {
+ return getRouteForIpPrefix(
+ new IpPrefix(
+ bytesToInet6Address(onMeshPrefixConfig.prefix),
+ onMeshPrefixConfig.prefixLength));
+ }
+
+ private RouteInfo getRouteForIpPrefix(IpPrefix ipPrefix) {
+ return new RouteInfo(ipPrefix, null, mIfName, RouteInfo.RTN_UNICAST, MTU);
}
/** Called by {@link ThreadNetworkControllerService} to do clean up when ot-daemon is dead. */
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index 998e70d..4028014 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -22,6 +22,8 @@
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
+import static android.net.thread.utils.IntegrationTestUtils.getPrefixesFromNetData;
+import static android.net.thread.utils.IntegrationTestUtils.getThreadNetwork;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
@@ -34,9 +36,11 @@
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
@@ -76,6 +80,9 @@
// The maximum time for OT addresses to be propagated to the TUN interface "thread-wpan"
private static final Duration TUN_ADDR_UPDATE_TIMEOUT = Duration.ofSeconds(1);
+ // The maximum time for changes to be propagated to netdata.
+ private static final Duration NET_DATA_UPDATE_TIMEOUT = Duration.ofSeconds(1);
+
// A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
private static final byte[] DEFAULT_DATASET_TLVS =
base16().decode(
@@ -90,6 +97,10 @@
private static final Inet6Address GROUP_ADDR_ALL_ROUTERS =
(Inet6Address) InetAddresses.parseNumericAddress("ff02::2");
+ private static final String TEST_NO_SLAAC_PREFIX = "9101:dead:beef:cafe::/64";
+ private static final InetAddress TEST_NO_SLAAC_PREFIX_ADDRESS =
+ InetAddresses.parseNumericAddress("9101:dead:beef:cafe::");
+
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private ExecutorService mExecutor;
@@ -253,6 +264,49 @@
}
}
+ @Test
+ public void addPrefixToNetData_routeIsAddedToTunInterface() throws Exception {
+ ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ mController.joinAndWait(DEFAULT_DATASET);
+
+ // Ftd child doesn't have the ability to add a prefix, so let BR itself add a prefix.
+ mOtCtl.executeCommand("prefix add " + TEST_NO_SLAAC_PREFIX + " pros med");
+ mOtCtl.executeCommand("netdata register");
+ waitFor(
+ () -> {
+ String netData = mOtCtl.executeCommand("netdata show");
+ return getPrefixesFromNetData(netData).contains(TEST_NO_SLAAC_PREFIX);
+ },
+ NET_DATA_UPDATE_TIMEOUT);
+
+ LinkProperties lp = cm.getLinkProperties(getThreadNetwork(CALLBACK_TIMEOUT));
+ assertThat(lp).isNotNull();
+ assertThat(lp.getRoutes().stream().anyMatch(r -> r.matches(TEST_NO_SLAAC_PREFIX_ADDRESS)))
+ .isTrue();
+ }
+
+ @Test
+ public void removePrefixFromNetData_routeIsRemovedFromTunInterface() throws Exception {
+ ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ mController.joinAndWait(DEFAULT_DATASET);
+ mOtCtl.executeCommand("prefix add " + TEST_NO_SLAAC_PREFIX + " pros med");
+ mOtCtl.executeCommand("netdata register");
+
+ mOtCtl.executeCommand("prefix remove " + TEST_NO_SLAAC_PREFIX);
+ mOtCtl.executeCommand("netdata register");
+ waitFor(
+ () -> {
+ String netData = mOtCtl.executeCommand("netdata show");
+ return !getPrefixesFromNetData(netData).contains(TEST_NO_SLAAC_PREFIX);
+ },
+ NET_DATA_UPDATE_TIMEOUT);
+
+ LinkProperties lp = cm.getLinkProperties(getThreadNetwork(CALLBACK_TIMEOUT));
+ assertThat(lp).isNotNull();
+ assertThat(lp.getRoutes().stream().anyMatch(r -> r.matches(TEST_NO_SLAAC_PREFIX_ADDRESS)))
+ .isFalse();
+ }
+
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService
diff --git a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
index 3a31ea5..46cf562 100644
--- a/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
+++ b/thread/tests/integration/src/android/net/thread/utils/FullThreadDevice.java
@@ -80,9 +80,10 @@
*/
public FullThreadDevice(int nodeId) {
try {
- mProcess = Runtime.getRuntime().exec("/system/bin/ot-cli-ftd " + nodeId);
+ mProcess = Runtime.getRuntime().exec("/system/bin/ot-cli-ftd -Leth1 " + nodeId);
} catch (IOException e) {
- throw new IllegalStateException("Failed to start ot-cli-ftd (id=" + nodeId + ")", e);
+ throw new IllegalStateException(
+ "Failed to start ot-cli-ftd -Leth1 (id=" + nodeId + ")", e);
}
mReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
mWriter = new BufferedWriter(new OutputStreamWriter(mProcess.getOutputStream()));
diff --git a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
index 9be9566..78f5770 100644
--- a/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
+++ b/thread/tests/integration/src/android/net/thread/utils/IntegrationTestUtils.java
@@ -15,6 +15,7 @@
*/
package android.net.thread.utils;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
@@ -24,17 +25,24 @@
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.LinkAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.TestNetworkInterface;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.thread.ThreadNetworkController;
+import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import androidx.annotation.NonNull;
+import androidx.test.core.app.ApplicationProvider;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.Icmpv6Header;
@@ -375,6 +383,36 @@
}
}
+ public static String getPrefixesFromNetData(String netData) {
+ int startIdx = netData.indexOf("Prefixes:");
+ int endIdx = netData.indexOf("Routes:");
+ return netData.substring(startIdx, endIdx);
+ }
+
+ public static Network getThreadNetwork(Duration timeout) throws Exception {
+ CompletableFuture<Network> networkFuture = new CompletableFuture<>();
+ ConnectivityManager cm =
+ ApplicationProvider.getApplicationContext()
+ .getSystemService(ConnectivityManager.class);
+ NetworkRequest.Builder networkRequestBuilder =
+ new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_THREAD);
+ // Before V, we need to explicitly set `NET_CAPABILITY_LOCAL_NETWORK` capability to request
+ // a Thread network.
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ networkRequestBuilder.addCapability(NET_CAPABILITY_LOCAL_NETWORK);
+ }
+ NetworkRequest networkRequest = networkRequestBuilder.build();
+ ConnectivityManager.NetworkCallback networkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ networkFuture.complete(network);
+ }
+ };
+ cm.registerNetworkCallback(networkRequest, networkCallback);
+ return networkFuture.get(timeout.toSeconds(), SECONDS);
+ }
+
private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener {
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {}
diff --git a/thread/tests/multidevices/Android.bp b/thread/tests/multidevices/Android.bp
new file mode 100644
index 0000000..050caa8
--- /dev/null
+++ b/thread/tests/multidevices/Android.bp
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2024 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_team: "trendy_team_fwk_thread_network",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test_host {
+ name: "ThreadNetworkMultiDeviceTests",
+ main: "thread_network_multi_device_test.py",
+ srcs: ["thread_network_multi_device_test.py"],
+ test_config: "AndroidTest.xml",
+ libs: [
+ "mobly",
+ ],
+ test_options: {
+ unit_test: false,
+ tags: ["mobly"],
+ },
+ test_suites: [
+ "mts-tethering",
+ "general-tests",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
diff --git a/thread/tests/multidevices/AndroidTest.xml b/thread/tests/multidevices/AndroidTest.xml
new file mode 100644
index 0000000..a2ea9aa
--- /dev/null
+++ b/thread/tests/multidevices/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 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.
+ -->
+
+<configuration description="Config for Thread Multi-device test cases">
+ <option name="config-descriptor:metadata" key="component" value="threadnetwork" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.tethering.apex" />
+
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.hardware.thread_network" />
+ </object>
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ </object>
+ <!--
+ Only run tests if the device under test is SDK version 34 (Android 14) or above.
+ -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.Sdk34ModuleController" />
+
+ <device name="device1">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ </device>
+ <device name="device2">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ </device>
+
+ <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
+ <!-- The mobly-par-file-name should match the module name -->
+ <option name="mobly-par-file-name" value="ThreadMultiDeviceTestCases" />
+ <!-- Timeout limit in milliseconds for all test cases of the python binary -->
+ <option name="mobly-test-timeout" value="180000" />
+ </test>
+</configuration>
diff --git a/thread/tests/multidevices/thread_network_multi_device_test.py b/thread/tests/multidevices/thread_network_multi_device_test.py
new file mode 100644
index 0000000..652576b
--- /dev/null
+++ b/thread/tests/multidevices/thread_network_multi_device_test.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2024 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.
+
+# Lint as: python3
+
+import logging
+import time
+
+from mobly import asserts
+from mobly import base_test
+from mobly import test_runner
+from mobly.controllers import android_device
+
+class ThreadNetworkMultiDeviceTest(base_test.BaseTestClass):
+ def setup_class(self):
+ self.node_a, self.node_b = self.register_controller(
+ android_device, min_number=2)
+ self.node_a.adb.shell([
+ 'ot-ctl', 'factoryreset',
+ ])
+ self.node_b.adb.shell([
+ 'ot-ctl', 'factoryreset',
+ ])
+ time.sleep(1)
+
+ def ot_ctl(self, node, cmd, expect_done=True):
+ args = cmd.split(' ')
+ args = ['ot-ctl'] + args
+ stdout = node.adb.shell(args).decode('utf-8')
+ if expect_done:
+ asserts.assert_in('Done', stdout)
+ return stdout
+
+ def test_b_should_be_able_to_discover_a(self):
+ self.ot_ctl(self.node_a, 'dataset init new')
+ self.ot_ctl(self.node_a, 'dataset commit active')
+ self.ot_ctl(self.node_a, 'ifconfig up')
+ self.ot_ctl(self.node_a, 'thread start')
+ self.ot_ctl(self.node_a, 'state leader')
+ stdout = self.ot_ctl(self.node_a, 'extaddr')
+ extaddr = stdout.splitlines()[0]
+ logging.info('node a extaddr: %s', extaddr)
+ asserts.assert_equal(len(extaddr), 16)
+
+ stdout = self.ot_ctl(self.node_b, 'scan')
+ asserts.assert_in(extaddr, stdout)
+ logging.info('discovered node a')
+
+
+if __name__ == '__main__':
+ # Take test args
+ if '--' in sys.argv:
+ index = sys.argv.index('--')
+ sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+
+ test_runner.main()