Merge "Avoid hardcoding BATTERY_PLUGGED_ANY constant."
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
index 5d2f6e5..e17081a 100644
--- a/Cronet/tests/common/Android.bp
+++ b/Cronet/tests/common/Android.bp
@@ -26,7 +26,6 @@
// go with merging NetHttp and Tethering targets.
android_test {
name: "NetHttpCoverageTests",
- defaults: ["CronetTestJavaDefaults"],
enforce_default_target_sdk_version: true,
min_sdk_version: "30",
test_suites: ["general-tests", "mts-tethering"],
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
index 7b440cd..1e0adef 100644
--- a/Cronet/tests/cts/Android.bp
+++ b/Cronet/tests/cts/Android.bp
@@ -18,38 +18,10 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// cronet_test_java_defaults can be used to specify a java_defaults target that
-// either enables or disables Cronet tests. This is used to disable Cronet
-// tests on tm-mainline-prod where the required APIs are not present.
-cronet_test_java_defaults = "CronetTestJavaDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_test_java_defaults may have different values
-// depending on the branch
-
-java_defaults {
- name: "CronetTestJavaDefaultsEnabled",
- enabled: true,
- // TODO(danstahr): move to unconditional static_libs once the T branch is abandoned
- static_libs: [
- "truth",
- ],
-}
-
-java_defaults {
- name: "CronetTestJavaDefaultsDisabled",
- enabled: false,
-}
-
-java_defaults {
- name: "CronetTestJavaDefaults",
- defaults: [cronet_test_java_defaults],
-}
-
android_library {
name: "CtsNetHttpTestsLib",
defaults: [
"cts_defaults",
- "CronetTestJavaDefaults",
],
sdk_version: "test_current",
min_sdk_version: "30",
@@ -61,15 +33,16 @@
"androidx.test.ext.junit",
"ctstestrunner-axt",
"ctstestserver",
- "junit",
"hamcrest-library",
+ "junit",
"kotlin-test",
"mockito-target",
+ "truth",
],
libs: [
"android.test.base",
"androidx.annotation_annotation",
- "framework-tethering",
+ "framework-connectivity",
"org.apache.http.legacy",
],
lint: { test: true }
@@ -79,9 +52,10 @@
name: "CtsNetHttpTestCases",
defaults: [
"cts_defaults",
- "CronetTestJavaDefaults",
],
+ 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: [
diff --git a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
index ed86854..31990fb 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
@@ -222,29 +222,16 @@
@Test
public void testHttpEngine_EnableQuic() throws Exception {
+ String url = mTestServer.getSuccessUrl();
mEngine = mEngineBuilder.setEnableQuic(true).addQuicHint(HOST, 443, 443).build();
- // The hint doesn't guarantee that QUIC will win the race, just that it will race TCP.
- // We send multiple requests to reduce the flakiness of the test.
- boolean quicWasUsed = false;
- for (int i = 0; i < 5; i++) {
- mCallback = new TestUrlRequestCallback();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
+ 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;
- assumeOKStatusCode(info);
- quicWasUsed = isQuic(info.getNegotiatedProtocol());
- if (quicWasUsed) {
- break;
- }
- }
- assertTrue(quicWasUsed);
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
}
@Test
@@ -379,34 +366,22 @@
@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();
- // The hint doesn't guarantee that QUIC will win the race, just that it will race TCP.
- // We send multiple requests to reduce the flakiness of the test.
- boolean quicWasUsed = false;
- for (int i = 0; i < 5; i++) {
- mCallback = new TestUrlRequestCallback();
- UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
- mRequest = builder.build();
- mRequest.start();
- mCallback.blockForDone();
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
- quicWasUsed = isQuic(mCallback.mResponseInfo.getNegotiatedProtocol());
- if (quicWasUsed) {
- break;
- }
- }
-
- assertTrue(quicWasUsed);
- // 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.
- assumeOKStatusCode(mCallback.mResponseInfo);
}
@Test
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index d13f695..4e4251c 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -19,7 +19,6 @@
java_genrule {
name: "net-http-test-jarjar-rules",
- defaults: ["CronetTestJavaDefaults"],
tool_files: [
":NetHttpTestsLibPreJarJar{.jar}",
"jarjar_excludes.txt",
@@ -30,38 +29,23 @@
out: ["net_http_test_jarjar_rules.txt"],
cmd: "$(location jarjar-rules-generator) " +
"$(location :NetHttpTestsLibPreJarJar{.jar}) " +
- "--prefix android.net.http.internal " +
+ "--prefix android.net.connectivity " +
"--excludes $(location jarjar_excludes.txt) " +
"--output $(out)",
}
+// Library to be used in coverage tests. cronet_java_tests can't be used directly because the common
+// tests need to inherit the NetHttpTests manifest.
android_library {
name: "NetHttpTestsLibPreJarJar",
- defaults: ["CronetTestJavaDefaults"],
- srcs: [":cronet_aml_javatests_sources"],
+ static_libs: ["cronet_java_tests"],
sdk_version: "module_current",
min_sdk_version: "30",
- static_libs: [
- "cronet_testserver_utils",
- "androidx.test.ext.junit",
- "androidx.test.rules",
- "junit",
- ],
- libs: [
- "android.test.base",
- "framework-tethering-pre-jarjar",
- // android.net.Network apis
- "framework-connectivity",
- // android.net.TrafficStats apis
- "framework-connectivity-t",
- ],
- lint: { test: true }
}
android_test {
name: "NetHttpTests",
defaults: [
- "CronetTestJavaDefaults",
"mts-target-sdk-version-current",
],
static_libs: ["NetHttpTestsLibPreJarJar"],
diff --git a/Cronet/tests/mts/jarjar_excludes.txt b/Cronet/tests/mts/jarjar_excludes.txt
index cf3a017..a3e86de 100644
--- a/Cronet/tests/mts/jarjar_excludes.txt
+++ b/Cronet/tests/mts/jarjar_excludes.txt
@@ -5,6 +5,8 @@
# 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(\$.+)?
+
# 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(\$.+)?
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d2f6d6a..c1d6a89 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -224,6 +224,9 @@
"exclude-annotation": "androidx.test.filters.LargeTest"
}
]
+ },
+ {
+ "name": "CtsTetheringTestLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
],
"mainline-postsubmit": [
@@ -231,6 +234,10 @@
{
"name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"keywords": ["sim"]
+ },
+ {
+ "name": "CtsTetheringTestLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+ "keywords": ["sim"]
}
],
"imports": [
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 83ca2b7..4478b1e 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -119,7 +119,6 @@
name: "libcom_android_networkstack_tethering_util_jni",
sdk_version: "30",
apex_available: [
- "//apex_available:platform", // Used by InProcessTethering
"com.android.tethering",
],
min_sdk_version: "30",
@@ -188,24 +187,6 @@
lint: { strict_updatability_linting: true },
}
-// Non-updatable tethering running in the system server process for devices not using the module
-android_app {
- name: "InProcessTethering",
- defaults: [
- "TetheringAppDefaults",
- "TetheringApiLevel",
- "ConnectivityNextEnableDefaults",
- "TetheringReleaseTargetSdk"
- ],
- static_libs: ["TetheringApiCurrentLib"],
- certificate: "platform",
- manifest: "AndroidManifest_InProcess.xml",
- // InProcessTethering is a replacement for Tethering
- overrides: ["Tethering"],
- apex_available: ["com.android.tethering"],
- lint: { strict_updatability_linting: true },
-}
-
// Updatable tethering packaged for finalized API
android_app {
name: "Tethering",
@@ -214,11 +195,7 @@
certificate: "networkstack",
manifest: "AndroidManifest.xml",
use_embedded_native_libs: true,
- // The network stack *must* be included to ensure security of the device
- required: [
- "NetworkStack",
- "privapp_allowlist_com.android.tethering",
- ],
+ privapp_allowlist: ":privapp_allowlist_com.android.tethering",
apex_available: ["com.android.tethering"],
lint: { strict_updatability_linting: true },
}
@@ -234,11 +211,7 @@
certificate: "networkstack",
manifest: "AndroidManifest.xml",
use_embedded_native_libs: true,
- // The network stack *must* be included to ensure security of the device
- required: [
- "NetworkStackNext",
- "privapp_allowlist_com.android.tethering",
- ],
+ privapp_allowlist: ":privapp_allowlist_com.android.tethering",
apex_available: ["com.android.tethering"],
lint: { strict_updatability_linting: true },
}
diff --git a/Tethering/AndroidManifest_InProcess.xml b/Tethering/AndroidManifest_InProcess.xml
deleted file mode 100644
index b1f1240..0000000
--- a/Tethering/AndroidManifest_InProcess.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack.tethering.inprocess"
- android:sharedUserId="android.uid.system"
- android:process="system">
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
- <application>
- <service android:name="com.android.networkstack.tethering.TetheringService"
- android:process="system"
- android:permission="android.permission.MAINLINE_NETWORK_STACK"
- android:exported="true">
- <intent-filter>
- <action android:name="android.net.ITetheringConnector.InProcess"/>
- </intent-filter>
- </service>
- </application>
-</manifest>
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 3f35c6b..cf9b359 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -18,20 +18,6 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-prebuilt_etc {
- name: "TetheringInProcessFlag",
- src: "in-process",
- filename_from_src: true,
- sub_dir: "flag",
-}
-
-prebuilt_etc {
- name: "TetheringOutOfProcessFlag",
- src: "out-of-process",
- filename_from_src: true,
- sub_dir: "flag",
-}
-
// Defaults to enable/disable java targets which uses development APIs. "enabled" may have a
// different value depending on the branch.
java_defaults {
@@ -57,22 +43,8 @@
// as the above target may have different "enabled" values
// depending on the branch
-// cronet_in_tethering_apex_defaults can be used to specify an apex_defaults target that either
-// enables or disables inclusion of Cronet in the Tethering apex. This is used to disable Cronet
-// on tm-mainline-prod. Note: in order for Cronet APIs to work Cronet must also be enabled
-// by the cronet_java_*_defaults in common/TetheringLib/Android.bp.
-cronet_in_tethering_apex_defaults = "CronetInTetheringApexDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_apex_defaults may have different values
-// depending on the branch
-
apex_defaults {
name: "CronetInTetheringApexDefaults",
- defaults: [cronet_in_tethering_apex_defaults],
-}
-
-apex_defaults {
- name: "CronetInTetheringApexDefaultsEnabled",
jni_libs: [
"cronet_aml_components_cronet_android_cronet",
"//external/cronet/third_party/boringssl:libcrypto",
@@ -90,10 +62,6 @@
},
}
-apex_defaults {
- name: "CronetInTetheringApexDefaultsDisabled",
-}
-
apex {
name: "com.android.tethering",
defaults: [
@@ -144,11 +112,7 @@
"ServiceConnectivityResources",
"HalfSheetUX",
],
- prebuilts: [
- "current_sdkinfo",
- "privapp_allowlist_com.android.tethering",
- "TetheringOutOfProcessFlag",
- ],
+ prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
key: "com.android.tethering.key",
// Indicates that pre-installed version of this apex can be compressed.
@@ -242,7 +206,6 @@
"android.net.apf",
"android.net.connectivity",
"android.net.http.apihelpers",
- "android.net.http.internal",
"android.net.netstats.provider",
"android.net.nsd",
"android.net.wear",
@@ -255,27 +218,3 @@
standalone_contents: ["service-connectivity"],
apex_available: ["com.android.tethering"],
}
-
-override_apex {
- name: "com.android.tethering.inprocess",
- base: "com.android.tethering",
- package_name: "com.android.tethering.inprocess",
- enabled: enable_tethering_next_apex,
- bpfs: [
- "block.o",
- "clatd.o",
- "dscpPolicy.o",
- "netd.o",
- "offload@inprocess.o",
- "test@inprocess.o",
- ],
- apps: [
- "ServiceConnectivityResources",
- "InProcessTethering",
- ],
- prebuilts: [
- "current_sdkinfo",
- "privapp_allowlist_com.android.tethering",
- "TetheringInProcessFlag",
- ],
-}
diff --git a/Tethering/apex/in-process b/Tethering/apex/in-process
deleted file mode 100644
index e69de29..0000000
--- a/Tethering/apex/in-process
+++ /dev/null
diff --git a/Tethering/apex/out-of-process b/Tethering/apex/out-of-process
deleted file mode 100644
index e69de29..0000000
--- a/Tethering/apex/out-of-process
+++ /dev/null
diff --git a/Tethering/apex/permissions/Android.bp b/Tethering/apex/permissions/Android.bp
index ac9ec65..69c1aa2 100644
--- a/Tethering/apex/permissions/Android.bp
+++ b/Tethering/apex/permissions/Android.bp
@@ -19,10 +19,7 @@
default_visibility: ["//packages/modules/Connectivity/Tethering:__subpackages__"],
}
-prebuilt_etc {
+filegroup {
name: "privapp_allowlist_com.android.tethering",
- sub_dir: "permissions",
- filename: "permissions.xml",
- src: "permissions.xml",
- installable: false,
+ srcs: ["permissions.xml"],
}
\ No newline at end of file
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 4f95bdd..a4db776 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -17,20 +17,9 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// Both cronet_java_defaults and cronet_java_prejarjar_defaults can be used to
-// specify a java_defaults target that either enables or disables Cronet. This
-// is used to disable Cronet on tm-mainline-prod.
-// Note: they must either both be enabled or disabled.
-cronet_java_defaults = "CronetJavaDefaultsEnabled"
-cronet_java_prejarjar_defaults = "CronetJavaPrejarjarDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_defaults may have different values
-// depending on the branch
-
java_sdk_library {
name: "framework-tethering",
defaults: [
- "CronetJavaDefaults",
"framework-tethering-defaults",
],
impl_library_visibility: [
@@ -68,49 +57,10 @@
lint: { strict_updatability_linting: true },
}
-java_defaults {
- name: "CronetJavaDefaults",
- defaults: [cronet_java_defaults],
-}
-
-java_defaults {
- name: "CronetJavaDefaultsEnabled",
- srcs: [":cronet_aml_api_sources"],
- libs: [
- "androidx.annotation_annotation",
- ],
- impl_only_static_libs: [
- "cronet_aml_java",
- ],
- api_dir: "cronet_enabled/api",
-}
-
-java_defaults {
- name: "CronetJavaDefaultsDisabled",
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaults",
- defaults: [cronet_java_prejarjar_defaults],
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaultsDisabled",
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaultsEnabled",
- static_libs: [
- "cronet_aml_api_java",
- "cronet_aml_java"
- ],
-}
-
java_library {
name: "framework-tethering-pre-jarjar",
defaults: [
"framework-tethering-defaults",
- "CronetJavaPrejarjarDefaults",
],
}
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt b/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
deleted file mode 100644
index 333ea1c..0000000
--- a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
+++ /dev/null
@@ -1,290 +0,0 @@
-// Signature format: 2.0
-package android.net.http {
-
- public abstract class BidirectionalStream {
- ctor public BidirectionalStream();
- method public abstract void cancel();
- method public abstract void flush();
- method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
- method @NonNull public abstract String getHttpMethod();
- method public abstract int getPriority();
- method public abstract int getTrafficStatsTag();
- method public abstract int getTrafficStatsUid();
- method public abstract boolean hasTrafficStatsTag();
- method public abstract boolean hasTrafficStatsUid();
- method public abstract boolean isDelayRequestHeadersUntilFirstFlushEnabled();
- method public abstract boolean isDone();
- method public abstract void read(@NonNull java.nio.ByteBuffer);
- method public abstract void start();
- method public abstract void write(@NonNull java.nio.ByteBuffer, boolean);
- field public static final int STREAM_PRIORITY_HIGHEST = 4; // 0x4
- field public static final int STREAM_PRIORITY_IDLE = 0; // 0x0
- field public static final int STREAM_PRIORITY_LOW = 2; // 0x2
- field public static final int STREAM_PRIORITY_LOWEST = 1; // 0x1
- field public static final int STREAM_PRIORITY_MEDIUM = 3; // 0x3
- }
-
- public abstract static class BidirectionalStream.Builder {
- ctor public BidirectionalStream.Builder();
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder addHeader(@NonNull String, @NonNull String);
- method @NonNull public abstract android.net.http.BidirectionalStream build();
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder setDelayRequestHeadersUntilFirstFlushEnabled(boolean);
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder setHttpMethod(@NonNull String);
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder setPriority(int);
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder setTrafficStatsTag(int);
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder setTrafficStatsUid(int);
- }
-
- public static interface BidirectionalStream.Callback {
- method public void onCanceled(@NonNull android.net.http.BidirectionalStream, @Nullable android.net.http.UrlResponseInfo);
- method public void onFailed(@NonNull android.net.http.BidirectionalStream, @Nullable android.net.http.UrlResponseInfo, @NonNull android.net.http.HttpException);
- method public void onReadCompleted(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer, boolean);
- method public void onResponseHeadersReceived(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo);
- method public void onResponseTrailersReceived(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull android.net.http.HeaderBlock);
- method public void onStreamReady(@NonNull android.net.http.BidirectionalStream);
- method public void onSucceeded(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo);
- method public void onWriteCompleted(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer, boolean);
- }
-
- public abstract class CallbackException extends android.net.http.HttpException {
- ctor protected CallbackException(@Nullable String, @Nullable Throwable);
- }
-
- public class ConnectionMigrationOptions {
- method public int getAllowNonDefaultNetworkUsage();
- method public int getDefaultNetworkMigration();
- method public int getPathDegradationMigration();
- field public static final int MIGRATION_OPTION_DISABLED = 2; // 0x2
- field public static final int MIGRATION_OPTION_ENABLED = 1; // 0x1
- field public static final int MIGRATION_OPTION_UNSPECIFIED = 0; // 0x0
- }
-
- public static final class ConnectionMigrationOptions.Builder {
- ctor public ConnectionMigrationOptions.Builder();
- method @NonNull public android.net.http.ConnectionMigrationOptions build();
- method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setAllowNonDefaultNetworkUsage(int);
- method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setDefaultNetworkMigration(int);
- method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setPathDegradationMigration(int);
- }
-
- public final class DnsOptions {
- method public int getPersistHostCache();
- method @Nullable public java.time.Duration getPersistHostCachePeriod();
- method public int getPreestablishConnectionsToStaleDnsResults();
- method public int getStaleDns();
- method @Nullable public android.net.http.DnsOptions.StaleDnsOptions getStaleDnsOptions();
- method public int getUseHttpStackDnsResolver();
- field public static final int DNS_OPTION_DISABLED = 2; // 0x2
- field public static final int DNS_OPTION_ENABLED = 1; // 0x1
- field public static final int DNS_OPTION_UNSPECIFIED = 0; // 0x0
- }
-
- public static final class DnsOptions.Builder {
- ctor public DnsOptions.Builder();
- method @NonNull public android.net.http.DnsOptions build();
- method @NonNull public android.net.http.DnsOptions.Builder setPersistHostCache(int);
- method @NonNull public android.net.http.DnsOptions.Builder setPersistHostCachePeriod(@NonNull java.time.Duration);
- method @NonNull public android.net.http.DnsOptions.Builder setPreestablishConnectionsToStaleDnsResults(int);
- method @NonNull public android.net.http.DnsOptions.Builder setStaleDns(int);
- method @NonNull public android.net.http.DnsOptions.Builder setStaleDnsOptions(@NonNull android.net.http.DnsOptions.StaleDnsOptions);
- method @NonNull public android.net.http.DnsOptions.Builder setUseHttpStackDnsResolver(int);
- }
-
- public static class DnsOptions.StaleDnsOptions {
- method public int getAllowCrossNetworkUsage();
- method @Nullable public java.time.Duration getFreshLookupTimeout();
- method @Nullable public java.time.Duration getMaxExpiredDelay();
- method public int getUseStaleOnNameNotResolved();
- }
-
- public static final class DnsOptions.StaleDnsOptions.Builder {
- ctor public DnsOptions.StaleDnsOptions.Builder();
- method @NonNull public android.net.http.DnsOptions.StaleDnsOptions build();
- method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setAllowCrossNetworkUsage(int);
- method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setFreshLookupTimeout(@NonNull java.time.Duration);
- method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setMaxExpiredDelay(@NonNull java.time.Duration);
- method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setUseStaleOnNameNotResolved(int);
- }
-
- public abstract class HeaderBlock {
- ctor public HeaderBlock();
- method @NonNull public abstract java.util.List<java.util.Map.Entry<java.lang.String,java.lang.String>> getAsList();
- method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> getAsMap();
- }
-
- public abstract class HttpEngine {
- method public void bindToNetwork(@Nullable android.net.Network);
- method @NonNull public abstract java.net.URLStreamHandlerFactory createUrlStreamHandlerFactory();
- method @NonNull public static String getVersionString();
- method @NonNull public abstract android.net.http.BidirectionalStream.Builder newBidirectionalStreamBuilder(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.http.BidirectionalStream.Callback);
- method @NonNull public abstract android.net.http.UrlRequest.Builder newUrlRequestBuilder(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.http.UrlRequest.Callback);
- method @NonNull public abstract java.net.URLConnection openConnection(@NonNull java.net.URL) throws java.io.IOException;
- method public abstract void shutdown();
- }
-
- public static class HttpEngine.Builder {
- ctor public HttpEngine.Builder(@NonNull android.content.Context);
- method @NonNull public android.net.http.HttpEngine.Builder addPublicKeyPins(@NonNull String, @NonNull java.util.Set<byte[]>, boolean, @NonNull java.time.Instant);
- method @NonNull public android.net.http.HttpEngine.Builder addQuicHint(@NonNull String, int, int);
- method @NonNull public android.net.http.HttpEngine build();
- method @NonNull public String getDefaultUserAgent();
- method @NonNull public android.net.http.HttpEngine.Builder setConnectionMigrationOptions(@NonNull android.net.http.ConnectionMigrationOptions);
- method @NonNull public android.net.http.HttpEngine.Builder setDnsOptions(@NonNull android.net.http.DnsOptions);
- method @NonNull public android.net.http.HttpEngine.Builder setEnableBrotli(boolean);
- method @NonNull public android.net.http.HttpEngine.Builder setEnableHttp2(boolean);
- method @NonNull public android.net.http.HttpEngine.Builder setEnableHttpCache(int, long);
- method @NonNull public android.net.http.HttpEngine.Builder setEnablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
- method @NonNull public android.net.http.HttpEngine.Builder setEnableQuic(boolean);
- method @NonNull public android.net.http.HttpEngine.Builder setQuicOptions(@NonNull android.net.http.QuicOptions);
- method @NonNull public android.net.http.HttpEngine.Builder setStoragePath(@NonNull String);
- method @NonNull public android.net.http.HttpEngine.Builder setUserAgent(@NonNull String);
- field public static final int HTTP_CACHE_DISABLED = 0; // 0x0
- field public static final int HTTP_CACHE_DISK = 3; // 0x3
- field public static final int HTTP_CACHE_DISK_NO_HTTP = 2; // 0x2
- field public static final int HTTP_CACHE_IN_MEMORY = 1; // 0x1
- }
-
- public class HttpException extends java.io.IOException {
- ctor public HttpException(@Nullable String, @Nullable Throwable);
- }
-
- public final class InlineExecutionProhibitedException extends java.util.concurrent.RejectedExecutionException {
- ctor public InlineExecutionProhibitedException();
- }
-
- public abstract class NetworkException extends android.net.http.HttpException {
- ctor public NetworkException(@Nullable String, @Nullable Throwable);
- method public abstract int getErrorCode();
- method public abstract boolean isImmediatelyRetryable();
- field public static final int ERROR_ADDRESS_UNREACHABLE = 9; // 0x9
- field public static final int ERROR_CONNECTION_CLOSED = 5; // 0x5
- field public static final int ERROR_CONNECTION_REFUSED = 7; // 0x7
- field public static final int ERROR_CONNECTION_RESET = 8; // 0x8
- field public static final int ERROR_CONNECTION_TIMED_OUT = 6; // 0x6
- field public static final int ERROR_HOSTNAME_NOT_RESOLVED = 1; // 0x1
- field public static final int ERROR_INTERNET_DISCONNECTED = 2; // 0x2
- field public static final int ERROR_NETWORK_CHANGED = 3; // 0x3
- field public static final int ERROR_OTHER = 11; // 0xb
- field public static final int ERROR_QUIC_PROTOCOL_FAILED = 10; // 0xa
- field public static final int ERROR_TIMED_OUT = 4; // 0x4
- }
-
- public abstract class QuicException extends android.net.http.NetworkException {
- ctor protected QuicException(@Nullable String, @Nullable Throwable);
- }
-
- public class QuicOptions {
- method @NonNull public java.util.Set<java.lang.String> getAllowedQuicHosts();
- method @Nullable public String getHandshakeUserAgent();
- method @Nullable public java.time.Duration getIdleConnectionTimeout();
- method public int getInMemoryServerConfigsCacheSize();
- method public boolean hasInMemoryServerConfigsCacheSize();
- }
-
- public static final class QuicOptions.Builder {
- ctor public QuicOptions.Builder();
- method @NonNull public android.net.http.QuicOptions.Builder addAllowedQuicHost(@NonNull String);
- method @NonNull public android.net.http.QuicOptions build();
- method @NonNull public android.net.http.QuicOptions.Builder setHandshakeUserAgent(@NonNull String);
- method @NonNull public android.net.http.QuicOptions.Builder setIdleConnectionTimeout(@NonNull java.time.Duration);
- method @NonNull public android.net.http.QuicOptions.Builder setInMemoryServerConfigsCacheSize(int);
- }
-
- public abstract class UploadDataProvider implements java.io.Closeable {
- ctor public UploadDataProvider();
- method public void close() throws java.io.IOException;
- method public abstract long getLength() throws java.io.IOException;
- method public abstract void read(@NonNull android.net.http.UploadDataSink, @NonNull java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract void rewind(@NonNull android.net.http.UploadDataSink) throws java.io.IOException;
- }
-
- public abstract class UploadDataSink {
- ctor public UploadDataSink();
- method public abstract void onReadError(@NonNull Exception);
- method public abstract void onReadSucceeded(boolean);
- method public abstract void onRewindError(@NonNull Exception);
- method public abstract void onRewindSucceeded();
- }
-
- public abstract class UrlRequest {
- method public abstract void cancel();
- method public abstract void followRedirect();
- method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
- method @Nullable public abstract String getHttpMethod();
- method public abstract int getPriority();
- method public abstract void getStatus(@NonNull android.net.http.UrlRequest.StatusListener);
- method public abstract int getTrafficStatsTag();
- method public abstract int getTrafficStatsUid();
- method public abstract boolean hasTrafficStatsTag();
- method public abstract boolean hasTrafficStatsUid();
- method public abstract boolean isCacheDisabled();
- method public abstract boolean isDirectExecutorAllowed();
- method public abstract boolean isDone();
- method public abstract void read(@NonNull java.nio.ByteBuffer);
- method public abstract void start();
- field public static final int REQUEST_PRIORITY_HIGHEST = 4; // 0x4
- field public static final int REQUEST_PRIORITY_IDLE = 0; // 0x0
- field public static final int REQUEST_PRIORITY_LOW = 2; // 0x2
- field public static final int REQUEST_PRIORITY_LOWEST = 1; // 0x1
- field public static final int REQUEST_PRIORITY_MEDIUM = 3; // 0x3
- }
-
- public abstract static class UrlRequest.Builder {
- method @NonNull public abstract android.net.http.UrlRequest.Builder addHeader(@NonNull String, @NonNull String);
- method @NonNull public abstract android.net.http.UrlRequest.Builder bindToNetwork(@Nullable android.net.Network);
- method @NonNull public abstract android.net.http.UrlRequest build();
- method @NonNull public abstract android.net.http.UrlRequest.Builder setCacheDisabled(boolean);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setDirectExecutorAllowed(boolean);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setHttpMethod(@NonNull String);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setPriority(int);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setTrafficStatsTag(int);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setTrafficStatsUid(int);
- method @NonNull public abstract android.net.http.UrlRequest.Builder setUploadDataProvider(@NonNull android.net.http.UploadDataProvider, @NonNull java.util.concurrent.Executor);
- }
-
- public static interface UrlRequest.Callback {
- method public void onCanceled(@NonNull android.net.http.UrlRequest, @Nullable android.net.http.UrlResponseInfo);
- method public void onFailed(@NonNull android.net.http.UrlRequest, @Nullable android.net.http.UrlResponseInfo, @NonNull android.net.http.HttpException);
- method public void onReadCompleted(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer) throws java.lang.Exception;
- method public void onRedirectReceived(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo, @NonNull String) throws java.lang.Exception;
- method public void onResponseStarted(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo) throws java.lang.Exception;
- method public void onSucceeded(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo);
- }
-
- public static class UrlRequest.Status {
- field public static final int CONNECTING = 10; // 0xa
- field public static final int DOWNLOADING_PAC_FILE = 5; // 0x5
- field public static final int ESTABLISHING_PROXY_TUNNEL = 8; // 0x8
- field public static final int IDLE = 0; // 0x0
- field public static final int INVALID = -1; // 0xffffffff
- field public static final int READING_RESPONSE = 14; // 0xe
- field public static final int RESOLVING_HOST = 9; // 0x9
- field public static final int RESOLVING_HOST_IN_PAC_FILE = 7; // 0x7
- field public static final int RESOLVING_PROXY_FOR_URL = 6; // 0x6
- field public static final int SENDING_REQUEST = 12; // 0xc
- field public static final int SSL_HANDSHAKE = 11; // 0xb
- field public static final int WAITING_FOR_AVAILABLE_SOCKET = 2; // 0x2
- field public static final int WAITING_FOR_CACHE = 4; // 0x4
- field public static final int WAITING_FOR_DELEGATE = 3; // 0x3
- field public static final int WAITING_FOR_RESPONSE = 13; // 0xd
- field public static final int WAITING_FOR_STALLED_SOCKET_POOL = 1; // 0x1
- }
-
- public static interface UrlRequest.StatusListener {
- method public void onStatus(int);
- }
-
- public abstract class UrlResponseInfo {
- ctor public UrlResponseInfo();
- method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
- method public abstract int getHttpStatusCode();
- method @NonNull public abstract String getHttpStatusText();
- method @NonNull public abstract String getNegotiatedProtocol();
- method public abstract long getReceivedByteCount();
- method @NonNull public abstract String getUrl();
- method @NonNull public abstract java.util.List<java.lang.String> getUrlChain();
- method public abstract boolean wasCached();
- }
-
-}
-
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/module-lib-current.txt b/Tethering/common/TetheringLib/cronet_enabled/api/module-lib-current.txt
deleted file mode 100644
index 460c216..0000000
--- a/Tethering/common/TetheringLib/cronet_enabled/api/module-lib-current.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-// Signature format: 2.0
-package android.net {
-
- public final class TetheringConstants {
- field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
- field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
- field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
- field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
- field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
- }
-
- public class TetheringManager {
- ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
- method public int getLastTetherError(@NonNull String);
- method @NonNull public String[] getTetherableBluetoothRegexs();
- method @NonNull public String[] getTetherableIfaces();
- method @NonNull public String[] getTetherableUsbRegexs();
- method @NonNull public String[] getTetherableWifiRegexs();
- method @NonNull public String[] getTetheredIfaces();
- method @NonNull public String[] getTetheringErroredIfaces();
- method public boolean isTetheringSupported();
- method public boolean isTetheringSupported(@NonNull String);
- method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
- method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @Deprecated public int tether(@NonNull String);
- method @Deprecated public int untether(@NonNull String);
- }
-
- public static interface TetheringManager.TetheredInterfaceCallback {
- method public void onAvailable(@NonNull String);
- method public void onUnavailable();
- }
-
- public static interface TetheringManager.TetheredInterfaceRequest {
- method public void release();
- }
-
- public static interface TetheringManager.TetheringEventCallback {
- method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- }
-
- @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
- method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
- method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
- method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
- }
-
-}
-
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/removed.txt b/Tethering/common/TetheringLib/cronet_enabled/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/Tethering/common/TetheringLib/cronet_enabled/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/system-current.txt b/Tethering/common/TetheringLib/cronet_enabled/api/system-current.txt
deleted file mode 100644
index 844ff64..0000000
--- a/Tethering/common/TetheringLib/cronet_enabled/api/system-current.txt
+++ /dev/null
@@ -1,117 +0,0 @@
-// Signature format: 2.0
-package android.net {
-
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
- public final class TetheringInterface implements android.os.Parcelable {
- ctor public TetheringInterface(int, @NonNull String);
- method public int describeContents();
- method @NonNull public String getInterface();
- method public int getType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheringInterface> CREATOR;
- }
-
- public class TetheringManager {
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
- field @Deprecated public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1
- field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
- }
-
- public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onError(@NonNull android.net.TetheringInterface, int);
- method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
- method public default void onOffloadStatusChanged(int);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.Set<android.net.TetheringInterface>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
- }
-
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method public int getConnectivityScope();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setConnectivityScope(int);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
-}
-
diff --git a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
index a878fa5..14e4b9a 100644
--- a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
@@ -18,21 +18,19 @@
#include <error.h>
#include <jni.h>
#include <linux/filter.h>
+#include <linux/ipv6.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <netjniutils/netjniutils.h>
#include <net/if.h>
#include <netinet/ether.h>
-#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <sys/socket.h>
#include <stdio.h>
-namespace android {
+#include <bpf/BpfClassic.h>
-static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
-static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
-static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+namespace android {
static void throwSocketException(JNIEnv *env, const char* msg, int error) {
jniThrowExceptionFmt(env, "java/net/SocketException", "%s: %s", msg, strerror(error));
@@ -42,18 +40,14 @@
uint32_t type) {
sock_filter filter_code[] = {
// Check header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+ BPF_LOAD_IPV6_U8(nexthdr),
+ BPF2_REJECT_IF_NOT_EQUAL(IPPROTO_ICMPV6),
// Check ICMPv6 type.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
+ BPF_LOAD_NET_RELATIVE_U8(sizeof(ipv6hdr) + offsetof(icmp6_hdr, icmp6_type)),
+ BPF2_REJECT_IF_NOT_EQUAL(type),
- // Accept.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
-
- // Reject.
- BPF_STMT(BPF_RET | BPF_K, 0)
+ BPF_ACCEPT,
};
const sock_fprog filter = {
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 65ea8e5..6affb62 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -17,6 +17,8 @@
package android.net.ip;
import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
@@ -405,8 +407,8 @@
/** Internals. */
- private boolean startIPv4() {
- return configureIPv4(true);
+ private boolean startIPv4(int scope) {
+ return configureIPv4(true, scope);
}
/**
@@ -616,7 +618,7 @@
}
private void stopIPv4() {
- configureIPv4(false);
+ configureIPv4(false /* enabled */, CONNECTIVITY_SCOPE_GLOBAL /* not used */);
// NOTE: All of configureIPv4() will be refactored out of existence
// into calls to InterfaceController, shared with startIPv4().
mInterfaceCtrl.clearIPv4Address();
@@ -627,11 +629,11 @@
mStaticIpv4ClientAddr = null;
}
- private boolean configureIPv4(boolean enabled) {
+ private boolean configureIPv4(boolean enabled, int scope) {
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
if (enabled) {
- mIpv4Address = requestIpv4Address(true /* useLastAddress */);
+ mIpv4Address = requestIpv4Address(scope, true /* useLastAddress */);
}
if (mIpv4Address == null) {
@@ -679,12 +681,12 @@
return (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) && !SdkLevel.isAtLeastT();
}
- private LinkAddress requestIpv4Address(final boolean useLastAddress) {
+ private LinkAddress requestIpv4Address(final int scope, final boolean useLastAddress) {
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
if (shouldNotConfigureBluetoothInterface()) return new LinkAddress(BLUETOOTH_IFACE_ADDR);
- return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
+ return mPrivateAddressCoordinator.requestDownstreamAddress(this, scope, useLastAddress);
}
private boolean startIPv6() {
@@ -998,67 +1000,6 @@
}
}
- private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
- if (!currentPrefix.contains(mIpv4Address.getAddress())
- || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
- Log.e(TAG, "Invalid prefix: " + currentPrefix);
- return;
- }
-
- final LinkAddress deprecatedLinkAddress = mIpv4Address;
- mIpv4Address = requestIpv4Address(false);
- if (mIpv4Address == null) {
- mLog.e("Fail to request a new downstream prefix");
- return;
- }
- final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
-
- // Add new IPv4 address on the interface.
- if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
- mLog.e("Failed to add new IP " + srvAddr);
- return;
- }
-
- // Remove deprecated routes from local network.
- removeRoutesFromLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
- mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
-
- // Add new routes to local network.
- addRoutesToLocalNetwork(
- Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
- mLinkProperties.addLinkAddress(mIpv4Address);
-
- // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
- // listen on the interface configured with new IPv4 address, that results DNS validation
- // failure of downstream client even if appropriate routes have been configured.
- try {
- mNetd.tetherApplyDnsInterfaces();
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to update local DNS caching server");
- return;
- }
- sendLinkProperties();
-
- // Notify DHCP server that new prefix/route has been applied on IpServer.
- final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
- (Inet4Address) mStaticIpv4ClientAddr.getAddress();
- final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
- srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
- try {
- mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
- @Override
- public void callback(int statusCode) {
- if (statusCode != STATUS_SUCCESS) {
- mLog.e("Error updating DHCP serving params: " + statusCode);
- }
- }
- });
- } catch (RemoteException e) {
- mLog.e("Error updating DHCP serving params", e);
- }
- }
-
private byte getHopLimit(String upstreamIface, int adjustTTL) {
try {
int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -1173,12 +1114,37 @@
mBpfCoordinator.stopMonitoring(this);
}
- class BaseServingState extends State {
+ abstract class BaseServingState extends State {
+ private final int mDesiredInterfaceState;
+
+ BaseServingState(int interfaceState) {
+ mDesiredInterfaceState = interfaceState;
+ }
+
@Override
public void enter() {
startConntrackMonitoring();
- if (!startIPv4()) {
+ startServingInterface();
+
+ if (mLastError != TETHER_ERROR_NO_ERROR) {
+ transitionTo(mInitialState);
+ }
+
+ if (DBG) Log.d(TAG, getStateString(mDesiredInterfaceState) + " serve " + mIfaceName);
+ sendInterfaceState(mDesiredInterfaceState);
+ }
+
+ private int getScope() {
+ if (mDesiredInterfaceState == STATE_TETHERED) {
+ return CONNECTIVITY_SCOPE_GLOBAL;
+ }
+
+ return CONNECTIVITY_SCOPE_LOCAL;
+ }
+
+ private void startServingInterface() {
+ if (!startIPv4(getScope())) {
mLastError = TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -1257,6 +1223,67 @@
}
return true;
}
+
+ private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+ if (!currentPrefix.contains(mIpv4Address.getAddress())
+ || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
+ Log.e(TAG, "Invalid prefix: " + currentPrefix);
+ return;
+ }
+
+ final LinkAddress deprecatedLinkAddress = mIpv4Address;
+ mIpv4Address = requestIpv4Address(getScope(), false);
+ if (mIpv4Address == null) {
+ mLog.e("Fail to request a new downstream prefix");
+ return;
+ }
+ final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
+
+ // Add new IPv4 address on the interface.
+ if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
+ mLog.e("Failed to add new IP " + srvAddr);
+ return;
+ }
+
+ // Remove deprecated routes from local network.
+ removeRoutesFromLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
+ mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
+
+ // Add new routes to local network.
+ addRoutesToLocalNetwork(
+ Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
+ mLinkProperties.addLinkAddress(mIpv4Address);
+
+ // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
+ // listen on the interface configured with new IPv4 address, that results DNS validation
+ // failure of downstream client even if appropriate routes have been configured.
+ try {
+ mNetd.tetherApplyDnsInterfaces();
+ } catch (ServiceSpecificException | RemoteException e) {
+ mLog.e("Failed to update local DNS caching server");
+ return;
+ }
+ sendLinkProperties();
+
+ // Notify DHCP server that new prefix/route has been applied on IpServer.
+ final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
+ (Inet4Address) mStaticIpv4ClientAddr.getAddress();
+ final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
+ srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
+ try {
+ mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int statusCode) {
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error updating DHCP serving params: " + statusCode);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ mLog.e("Error updating DHCP serving params", e);
+ }
+ }
}
// Handling errors in BaseServingState.enter() by transitioning is
@@ -1265,15 +1292,8 @@
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of IpServer.
class LocalHotspotState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
- sendInterfaceState(STATE_LOCAL_ONLY);
+ LocalHotspotState() {
+ super(STATE_LOCAL_ONLY);
}
@Override
@@ -1301,15 +1321,8 @@
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of IpServer.
class TetheredState extends BaseServingState {
- @Override
- public void enter() {
- super.enter();
- if (mLastError != TETHER_ERROR_NO_ERROR) {
- transitionTo(mInitialState);
- }
-
- if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
- sendInterfaceState(STATE_TETHERED);
+ TetheredState() {
+ super(STATE_TETHERED);
}
@Override
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 775c36f..18c2171 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -129,9 +129,6 @@
// Tethered traffic will have the hop limit properly decremented.
// Consequently, set the hoplimit greater by one than the upstream
// unicast hop limit.
- //
- // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
- // upstream interface for more correct behaviour.
static final byte DEFAULT_HOPLIMIT = 65;
public boolean hasDefaultRoute;
diff --git a/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
index 8a96988..5b19c4e 100644
--- a/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
+++ b/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
@@ -29,6 +29,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -49,6 +51,8 @@
@NonNull
private List<WifiClient> mLastWifiClients = Collections.emptyList();
@NonNull
+ private List<WifiClient> mLastLocalOnlyClients = Collections.emptyList();
+ @NonNull
private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
@VisibleForTesting
@@ -72,25 +76,44 @@
*
* <p>The new list can be obtained through {@link #getLastTetheredClients()}.
* @param ipServers The IpServers used to assign addresses to clients.
- * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
- * update.
+ * @param wifiClients The list of L2-connected WiFi clients that are connected to a global
+ * hotspot. Null for no change since last update.
+ * @param localOnlyClients The list of L2-connected WiFi clients that are connected to localOnly
+ * hotspot. Null for no change since last update.
* @return True if the list of clients changed since the last calculation.
*/
public boolean updateConnectedClients(
- Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
+ Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients,
+ @Nullable List<WifiClient> localOnlyClients) {
final long now = mClock.elapsedRealtime();
- if (wifiClients != null) {
- mLastWifiClients = wifiClients;
- }
+ if (wifiClients != null) mLastWifiClients = wifiClients;
+ if (localOnlyClients != null) mLastLocalOnlyClients = localOnlyClients;
+
final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
+ final Set<MacAddress> localOnlyClientMacs = getClientMacs(mLastLocalOnlyClients);
// Build the list of non-expired leases from all IpServers, grouped by mac address
final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
for (IpServer server : ipServers) {
+ final Set<MacAddress> connectedClientMacs;
+ switch (server.servingMode()) {
+ case IpServer.STATE_TETHERED:
+ connectedClientMacs = wifiClientMacs;
+ break;
+ case IpServer.STATE_LOCAL_ONLY:
+ // Before T, SAP and LOHS both use wifiClientMacs because
+ // registerLocalOnlyHotspotSoftApCallback didn't exist.
+ connectedClientMacs = SdkLevel.isAtLeastT()
+ ? localOnlyClientMacs : wifiClientMacs;
+ break;
+ default:
+ continue;
+ }
+
for (TetheredClient client : server.getAllLeases()) {
if (client.getTetheringType() == TETHERING_WIFI
- && !wifiClientMacs.contains(client.getMacAddress())) {
+ && !connectedClientMacs.contains(client.getMacAddress())) {
// Skip leases of WiFi clients that are not (or no longer) L2-connected
continue;
}
@@ -104,11 +127,8 @@
// TODO: add IPv6 addresses from netlink
// Add connected WiFi clients that do not have any known address
- for (MacAddress client : wifiClientMacs) {
- if (clientsMap.containsKey(client)) continue;
- clientsMap.put(client, new TetheredClient(
- client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
- }
+ addWifiClientsIfNoLeases(clientsMap, wifiClientMacs);
+ addWifiClientsIfNoLeases(clientsMap, localOnlyClientMacs);
final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
@@ -117,6 +137,15 @@
return clientsChanged;
}
+ private static void addWifiClientsIfNoLeases(
+ final Map<MacAddress, TetheredClient> clientsMap, final Set<MacAddress> clientMacs) {
+ for (MacAddress mac : clientMacs) {
+ if (clientsMap.containsKey(mac)) continue;
+ clientsMap.put(mac, new TetheredClient(
+ mac, Collections.emptyList() /* addresses */, TETHERING_WIFI));
+ }
+ }
+
private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
final TetheredClient aggregateClient = clientsMap.getOrDefault(
lease.getMacAddress(), lease);
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 6d502ce..b88b13b 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -459,8 +459,9 @@
}
@VisibleForTesting
- PendingIntent createRecheckAlarmIntent() {
+ PendingIntent createRecheckAlarmIntent(final String pkgName) {
final Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
+ intent.setPackage(pkgName);
return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
@@ -470,7 +471,7 @@
final int period = config.provisioningCheckPeriod;
if (period <= 0) return;
- mProvisioningRecheckAlarm = createRecheckAlarmIntent();
+ mProvisioningRecheckAlarm = createRecheckAlarmIntent(mContext.getPackageName());
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
long triggerAtMillis = SystemClock.elapsedRealtime() + (period * MS_PER_HOUR);
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index 5fa6b2d..b4c0d6a 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -762,6 +762,16 @@
String upstream = (lp != null) ? lp.getInterfaceName() : null;
pw.println("Current upstream: " + upstream);
pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
+ pw.println("ForwardedStats:");
+ pw.increaseIndent();
+ if (mForwardedStats.isEmpty()) {
+ pw.println("<empty>");
+ } else {
+ for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+ pw.println(kv.getKey() + ": " + kv.getValue());
+ }
+ }
+ pw.decreaseIndent();
pw.println("NAT timeout update callbacks received during the "
+ (isStarted ? "current" : "last")
+ " offload session: "
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 41a10ae..6c0ca82 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -16,6 +16,8 @@
package com.android.networkstack.tethering;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -34,7 +36,6 @@
import android.net.ip.IpServer;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -77,7 +78,7 @@
private final ConnectivityManager mConnectivityMgr;
private final TetheringConfiguration mConfig;
// keyed by downstream type(TetheringManager.TETHERING_*).
- private final SparseArray<LinkAddress> mCachedAddresses;
+ private final ArrayMap<AddressKey, LinkAddress> mCachedAddresses;
public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
mDownstreams = new ArraySet<>();
@@ -85,10 +86,12 @@
mConnectivityMgr = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
mConfig = config;
- mCachedAddresses = new SparseArray<>();
+ mCachedAddresses = new ArrayMap<AddressKey, LinkAddress>();
// Reserved static addresses for bluetooth and wifi p2p.
- mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
- mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
+ mCachedAddresses.put(new AddressKey(TETHERING_BLUETOOTH, CONNECTIVITY_SCOPE_GLOBAL),
+ new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
+ mCachedAddresses.put(new AddressKey(TETHERING_WIFI_P2P, CONNECTIVITY_SCOPE_LOCAL),
+ new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"),
new IpPrefix("172.16.0.0/12"), new IpPrefix("10.0.0.0/8")));
@@ -166,16 +169,18 @@
* returns null if there is no available address.
*/
@Nullable
- public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+ public LinkAddress requestDownstreamAddress(final IpServer ipServer, final int scope,
+ boolean useLastAddress) {
if (mConfig.shouldEnableWifiP2pDedicatedIp()
&& ipServer.interfaceType() == TETHERING_WIFI_P2P) {
return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
}
+ final AddressKey addrKey = new AddressKey(ipServer.interfaceType(), scope);
// This ensures that tethering isn't started on 2 different interfaces with the same type.
// Once tethering could support multiple interface with the same type,
// TetheringSoftApCallback would need to handle it among others.
- final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
+ final LinkAddress cachedAddress = mCachedAddresses.get(addrKey);
if (useLastAddress && cachedAddress != null
&& !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
mDownstreams.add(ipServer);
@@ -186,7 +191,7 @@
final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
if (newAddress != null) {
mDownstreams.add(ipServer);
- mCachedAddresses.put(ipServer.interfaceType(), newAddress);
+ mCachedAddresses.put(addrKey, newAddress);
return newAddress;
}
}
@@ -384,6 +389,34 @@
return asIpPrefix(address);
}
+ private static class AddressKey {
+ private final int mTetheringType;
+ private final int mScope;
+
+ private AddressKey(int type, int scope) {
+ mTetheringType = type;
+ mScope = scope;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mTetheringType << 16) + mScope;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof AddressKey)) return false;
+ final AddressKey other = (AddressKey) obj;
+
+ return mTetheringType == other.mTetheringType && mScope == other.mScope;
+ }
+
+ @Override
+ public String toString() {
+ return "AddressKey(" + mTetheringType + ", " + mScope + ")";
+ }
+ }
+
void dump(final IndentingPrintWriter pw) {
pw.println("mTetheringPrefixes:");
pw.increaseIndent();
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 4c5bf4e..e5f644e 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -58,6 +58,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
+import static android.net.wifi.WifiManager.SoftApCallback;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -479,15 +480,15 @@
mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
final WifiManager wifiManager = getWifiManager();
- TetheringSoftApCallback softApCallback = new TetheringSoftApCallback();
if (wifiManager != null) {
- wifiManager.registerSoftApCallback(mExecutor, softApCallback);
- }
- if (SdkLevel.isAtLeastT() && wifiManager != null) {
- // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
- // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
- // or MAINLINE_NETWORK_STACK permission would also able to use this API.
- wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor, softApCallback);
+ wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+ if (SdkLevel.isAtLeastT()) {
+ // Although WifiManager#registerLocalOnlyHotspotSoftApCallback document that it need
+ // NEARBY_WIFI_DEVICES permission, but actually a caller who have NETWORK_STACK
+ // or MAINLINE_NETWORK_STACK permission can also use this API.
+ wifiManager.registerLocalOnlyHotspotSoftApCallback(mExecutor,
+ new LocalOnlyHotspotCallback());
+ }
}
startTrackDefaultNetwork();
@@ -573,26 +574,17 @@
}
}
- private class TetheringSoftApCallback implements WifiManager.SoftApCallback {
- // TODO: Remove onStateChanged override when this method has default on
- // WifiManager#SoftApCallback interface.
- // Wifi listener for state change of the soft AP
- @Override
- public void onStateChanged(final int state, final int failureReason) {
- // Nothing
- }
-
- // Called by wifi when the number of soft AP clients changed.
- // Currently multiple softAp would not behave well in PrivateAddressCoordinator
- // (where it gets the address from cache), it ensure tethering only support one ipServer for
- // TETHERING_WIFI. Once tethering support multiple softAp enabled simultaneously,
- // onConnectedClientsChanged should also be updated to support tracking different softAp's
- // clients individually.
- // TODO: Add wtf log and have check to reject request duplicated type with different
- // interface.
+ private class TetheringSoftApCallback implements SoftApCallback {
@Override
public void onConnectedClientsChanged(final List<WifiClient> clients) {
- updateConnectedClients(clients);
+ updateConnectedClients(clients, null);
+ }
+ }
+
+ private class LocalOnlyHotspotCallback implements SoftApCallback {
+ @Override
+ public void onConnectedClientsChanged(final List<WifiClient> clients) {
+ updateConnectedClients(null, clients);
}
}
@@ -1968,7 +1960,7 @@
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
mForwardedDownstreams.remove(who);
- updateConnectedClients(null /* wifiClients */);
+ maybeDhcpLeasesChanged();
// If this is a Wi-Fi interface, tell WifiManager of any errors
// or the inactive serving state.
@@ -2710,9 +2702,15 @@
if (e != null) throw e;
}
- private void updateConnectedClients(final List<WifiClient> wifiClients) {
+ private void maybeDhcpLeasesChanged() {
+ // null means wifi clients did not change.
+ updateConnectedClients(null, null);
+ }
+
+ private void updateConnectedClients(final List<WifiClient> wifiClients,
+ final List<WifiClient> localOnlyClients) {
if (mConnectedClientsTracker.updateConnectedClients(mTetherMainSM.getAllDownstreams(),
- wifiClients)) {
+ wifiClients, localOnlyClients)) {
reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
}
}
@@ -2731,7 +2729,7 @@
@Override
public void dhcpLeasesChanged() {
- updateConnectedClients(null /* wifiClients */);
+ maybeDhcpLeasesChanged();
}
@Override
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index b6591a9..b0aa668 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -39,6 +39,8 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.DeviceConfigUtils;
@@ -158,6 +160,8 @@
public final int activeDataSubId;
+ private final Dependencies mDeps;
+
private final boolean mEnableLegacyDhcpServer;
private final int mOffloadPollInterval;
// TODO: Add to TetheringConfigurationParcel if required.
@@ -170,7 +174,31 @@
private final int mUsbTetheringFunction;
protected final ContentResolver mContentResolver;
- public TetheringConfiguration(Context ctx, SharedLog log, int id) {
+ /**
+ * A class wrapping dependencies of {@link TetheringConfiguration}, useful for testing.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, @NonNull String moduleName, boolean defaultEnabled) {
+ return DeviceConfigUtils.isFeatureEnabled(context, namespace, name,
+ moduleName, defaultEnabled);
+ }
+
+ boolean getDeviceConfigBoolean(@NonNull String namespace, @NonNull String name,
+ boolean defaultValue) {
+ return DeviceConfig.getBoolean(namespace, name, defaultValue);
+ }
+ }
+
+ public TetheringConfiguration(@NonNull Context ctx, @NonNull SharedLog log, int id) {
+ this(ctx, log, id, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public TetheringConfiguration(@NonNull Context ctx, @NonNull SharedLog log, int id,
+ @NonNull Dependencies deps) {
+ mDeps = deps;
final SharedLog configLog = log.forSubComponent("config");
activeDataSubId = id;
@@ -583,17 +611,7 @@
}
private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
- // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
- // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
- // returned boolean value comes from device config or default value (because of null
- // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
- final String value = getDeviceConfigProperty(name);
- return value != null ? Boolean.parseBoolean(value) : defaultValue;
- }
-
- @VisibleForTesting
- protected String getDeviceConfigProperty(String name) {
- return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
+ return mDeps.getDeviceConfigBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue);
}
/**
@@ -610,10 +628,9 @@
return isFeatureEnabled(ctx, NAMESPACE_TETHERING, featureVersionFlag);
}
- @VisibleForTesting
- protected boolean isFeatureEnabled(Context ctx, String namespace, String featureVersionFlag) {
- return DeviceConfigUtils.isFeatureEnabled(ctx, namespace, featureVersionFlag,
- TETHERING_MODULE_NAME, false /* defaultEnabled */);
+ private boolean isFeatureEnabled(Context ctx, String namespace, String featureVersionFlag) {
+ return mDeps.isFeatureEnabled(ctx, namespace, featureVersionFlag, TETHERING_MODULE_NAME,
+ false /* defaultEnabled */);
}
private Resources getResources(Context ctx, int subId) {
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index de25ff5..814afcd 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -166,10 +166,13 @@
* @param upstream the type of upstream type (e.g. Wifi, Cellular, Bluetooth, ...)
*/
private void addUpstreamEvent(final UpstreamEvents.Builder upstreamEventsBuilder,
- final long start, final long stop, @Nullable final UpstreamType upstream) {
+ final long start, final long stop, @Nullable final UpstreamType upstream,
+ final long txBytes, final long rxBytes) {
final UpstreamEvent.Builder upstreamEventBuilder = UpstreamEvent.newBuilder()
.setUpstreamType(upstream == null ? UpstreamType.UT_NO_NETWORK : upstream)
- .setDurationMillis(stop - start);
+ .setDurationMillis(stop - start)
+ .setTxBytes(txBytes)
+ .setRxBytes(rxBytes);
upstreamEventsBuilder.addUpstreamEvent(upstreamEventBuilder);
}
@@ -193,18 +196,20 @@
private void noteDownstreamStopped(final NetworkTetheringReported.Builder statsBuilder,
final long downstreamStartTime) {
UpstreamEvents.Builder upstreamEventsBuilder = UpstreamEvents.newBuilder();
+
for (RecordUpstreamEvent event : mUpstreamEventList) {
if (downstreamStartTime > event.mStopTime) continue;
final long startTime = Math.max(downstreamStartTime, event.mStartTime);
// Handle completed upstream events.
addUpstreamEvent(upstreamEventsBuilder, startTime, event.mStopTime,
- event.mUpstreamType);
+ event.mUpstreamType, 0L /* txBytes */, 0L /* rxBytes */);
}
final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
final long stopTime = timeNow();
// Handle the last upstream event.
- addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream);
+ addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream,
+ 0L /* txBytes */, 0L /* rxBytes */);
statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
index 27f2126..b276389 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/stats.proto
@@ -21,13 +21,21 @@
import "frameworks/proto_logging/stats/enums/stats/connectivity/tethering.proto";
-// Logs each upstream for a successful switch over
+/**
+ * Represents an event that logs information about a successful switch to an upstream network.
+ */
message UpstreamEvent {
- // Transport type of upstream network
+ // Indicates the transport type of network.
optional .android.stats.connectivity.UpstreamType upstream_type = 1;
- // A time period that an upstream continued
+ // The duration of network usage.
optional int64 duration_millis = 2;
+
+ // The amount of data received from tethered clients.
+ optional int64 tx_bytes = 3;
+
+ // The amount of data received from remote.
+ optional int64 rx_bytes = 4;
}
message UpstreamEvents {
diff --git a/Tethering/tests/integration/base/android/net/TetheringTester.java b/Tethering/tests/integration/base/android/net/TetheringTester.java
index ae39b24..1c0803e 100644
--- a/Tethering/tests/integration/base/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/base/android/net/TetheringTester.java
@@ -628,7 +628,7 @@
return false;
}
- private void sendUploadPacket(ByteBuffer packet) throws Exception {
+ public void sendUploadPacket(ByteBuffer packet) throws Exception {
mDownstreamReader.sendResponse(packet);
}
@@ -680,4 +680,12 @@
return verifyPacketNotNull("Download fail", getDownloadPacket(filter));
}
+
+ // Send DHCPDISCOVER to DHCP server to see if DHCP server is still alive to handle
+ // the upcoming DHCP packets. This method should be only used when we know the DHCP
+ // server has been created successfully before.
+ public boolean testDhcpServerAlive(final MacAddress mac) throws Exception {
+ sendDhcpDiscover(mac.toByteArray());
+ return getNextDhcpPacket() != null;
+ }
}
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 55854e2..4ee5c42 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -839,4 +839,41 @@
REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */,
tester, true /* isClat */);
}
+
+ private static final byte[] ZeroLengthDhcpPacket = new byte[] {
+ // scapy.Ether(
+ // dst="ff:ff:ff:ff:ff:ff")
+ // scapy.IP(
+ // dst="255.255.255.255")
+ // scapy.UDP(sport=68, dport=67)
+ /* Ethernet Header */
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xe0, (byte) 0x4f, (byte) 0x43, (byte) 0xe6, (byte) 0xfb, (byte) 0xd2,
+ (byte) 0x08, (byte) 0x00,
+ /* Ip header */
+ (byte) 0x45, (byte) 0x00, (byte) 0x00, (byte) 0x1c, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0xb6, (byte) 0x58,
+ (byte) 0x64, (byte) 0x4f, (byte) 0x60, (byte) 0x29, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ /* UDP header */
+ (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
+ (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf
+ };
+
+ @Test
+ public void testTetherZeroLengthDhcpPacket() throws Exception {
+ final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+ toList(TEST_IP4_DNS));
+ tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+ // Send a zero-length DHCP packet to upstream DHCP server.
+ final ByteBuffer packet = ByteBuffer.wrap(ZeroLengthDhcpPacket);
+ tester.sendUploadPacket(packet);
+
+ // Send DHCPDISCOVER packet from another downstream tethered device to verify that
+ // upstream DHCP server doesn't close the listening socket and stop reading, then we
+ // can still receive the next DHCP packet from server.
+ final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66");
+ assertTrue(tester.testDhcpServerAlive(macAddress));
+ }
}
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
index b3fb3e4..81d4fbe 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -106,6 +106,7 @@
ConntrackMessage.Tuple tuple = ctmsg.tupleOrig;
if (nlmsghdr.nlmsg_type == (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW)
+ && tuple != null
&& tuple.protoNum == IPPROTO_TCP
&& tuple.srcIp.equals(local.getAddress())
&& tuple.dstIp.equals(remote.getAddress())
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f0d9057..46e50ef 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -19,6 +19,8 @@
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
@@ -48,6 +50,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -271,8 +274,8 @@
dispatchTetherConnectionChanged(upstreamIface, lp, 0);
}
reset(mNetd, mCallback, mAddressCoordinator, mBpfCoordinator);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(mTestAddress);
}
@SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run()
@@ -293,8 +296,8 @@
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- mTestAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(mTestAddress);
when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
@@ -428,7 +431,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
if (isAtLeastT()) {
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
}
@@ -477,7 +481,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -498,7 +503,8 @@
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(true));
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -766,7 +772,8 @@
final ArgumentCaptor<LinkProperties> lpCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(true));
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
// One for ipv4 route, one for ipv6 link local route.
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
@@ -779,12 +786,13 @@
// Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
// onNewPrefixRequest callback.
final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
- when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
- newAddress);
+ when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(),
+ anyBoolean())).thenReturn(newAddress);
eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
mLooper.dispatchAll();
- inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false));
+ inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(),
+ eq(CONNECTIVITY_SCOPE_LOCAL), eq(false));
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
verifyNoMoreInteractions(mCallback);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
index d915354..2dd9f91 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
@@ -24,19 +24,25 @@
import android.net.TetheringManager.TETHERING_WIFI
import android.net.ip.IpServer
import android.net.wifi.WifiClient
+import android.os.Build
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
@SmallTest
class ConnectedClientsTrackerTest {
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule()
private val server1 = mock(IpServer::class.java)
private val server2 = mock(IpServer::class.java)
@@ -70,55 +76,122 @@
@Test
fun testUpdateConnectedClients() {
+ doReturn(IpServer.STATE_TETHERED).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
+ runUpdateConnectedClientsTest(isGlobal = true)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ fun testUpdateConnectedClients_LocalOnly() {
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server2).servingMode()
+ runUpdateConnectedClientsTest(isGlobal = false)
+ }
+
+ fun runUpdateConnectedClientsTest(isGlobal: Boolean) {
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
val tracker = ConnectedClientsTracker(clock)
- assertFalse(tracker.updateConnectedClients(servers, null))
+ assertFalse(tracker.updateConnectedClients(servers, null, null))
// Obtain a lease for client 1
doReturn(listOf(client1)).`when`(server1).allLeases
- assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1)))
+ if (isGlobal) {
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ wifiClients = listOf(wifiClient1)))
+ } else {
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ localOnlyClients = listOf(wifiClient1)))
+ }
// Client 2 L2-connected, no lease yet
val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI)
- assertSameClients(listOf(client1, client2WithoutAddr),
- assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2)))
+ if (isGlobal) {
+ assertSameClients(listOf(client1, client2WithoutAddr), assertNewClients(
+ tracker, servers, wifiClients = listOf(wifiClient1, wifiClient2)))
+ } else {
+ assertSameClients(listOf(client1, client2WithoutAddr), assertNewClients(
+ tracker, servers, localOnlyClients = listOf(wifiClient1, wifiClient2)))
+ }
// Client 2 lease obtained
doReturn(listOf(client1, client2)).`when`(server1).allLeases
- assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null))
+ assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers))
// Client 3 lease obtained
doReturn(listOf(client3)).`when`(server2).allLeases
- assertSameClients(listOf(client1, client2, client3),
- assertNewClients(tracker, servers, null))
+ assertSameClients(listOf(client1, client2, client3), assertNewClients(tracker, servers))
- // Client 2 L2-disconnected
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
-
- // Client 1 L2-disconnected
- assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList()))
-
- // Client 1 comes back
- assertSameClients(listOf(client1, client3),
- assertNewClients(tracker, servers, listOf(wifiClient1)))
+ if (isGlobal) {
+ // Client 2 L2-disconnected
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, wifiClients = listOf(wifiClient1)))
+ // Client 1 L2-disconnected
+ assertSameClients(listOf(client3), assertNewClients(tracker, servers,
+ wifiClients = emptyList()))
+ // Client 1 comes back
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, wifiClients = listOf(wifiClient1)))
+ } else {
+ // Client 2 L2-disconnected
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, localOnlyClients = listOf(wifiClient1)))
+ // Client 1 L2-disconnected
+ assertSameClients(listOf(client3),
+ assertNewClients(tracker, servers, localOnlyClients = emptyList()))
+ // Client 1 comes back
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, localOnlyClients = listOf(wifiClient1)))
+ }
// Leases lost, client 1 still L2-connected
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)),
- assertNewClients(tracker, servers, null))
+ assertNewClients(tracker, servers))
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ fun testLocalOnlyAndTetheredHotspotClients() {
+ val tracker = ConnectedClientsTracker(clock)
+ doReturn(IpServer.STATE_LOCAL_ONLY).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
+
+ // Client 1 connected to server1 (LOHS)
+ doReturn(listOf(client1)).`when`(server1).allLeases
+ doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ localOnlyClients = listOf(wifiClient1)))
+
+ // Client 2 connected to server2 (wifi Tethering)
+ doReturn(listOf(client2)).`when`(server2).allLeases
+ assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers,
+ listOf(wifiClient2), listOf(wifiClient1)))
+
+ // Client 2 L2-disconnected but lease doesn't expired yet
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers,
+ wifiClients = emptyList()))
+
+ // Client 1 lease lost but still L2-connected
+ doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+ val client1WithoutAddr = TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)
+ assertSameClients(listOf(client1WithoutAddr), assertNewClients(tracker, servers))
+
+ // Client 1 L2-disconnected
+ assertSameClients(emptyList(), assertNewClients(tracker, servers,
+ localOnlyClients = emptyList()))
}
@Test
fun testUpdateConnectedClients_LeaseExpiration() {
+ doReturn(IpServer.STATE_TETHERED).`when`(server1).servingMode()
+ doReturn(IpServer.STATE_TETHERED).`when`(server2).servingMode()
val tracker = ConnectedClientsTracker(clock)
doReturn(listOf(client1, client2)).`when`(server1).allLeases
doReturn(listOf(client3)).`when`(server2).allLeases
assertSameClients(listOf(client1, client2, client3), assertNewClients(
- tracker, servers, listOf(wifiClient1, wifiClient2)))
+ tracker, servers, wifiClients = listOf(wifiClient1, wifiClient2)))
clock.time += 20
// Client 3 has no remaining lease: removed
@@ -131,15 +204,16 @@
// Only the "t + 30" address is left, the "t + 10" address expired
listOf(client2Exp30AddrInfo),
TETHERING_WIFI))
- assertSameClients(expectedClients, assertNewClients(tracker, servers, null))
+ assertSameClients(expectedClients, assertNewClients(tracker, servers))
}
private fun assertNewClients(
tracker: ConnectedClientsTracker,
ipServers: Iterable<IpServer>,
- wifiClients: List<WifiClient>?
+ wifiClients: List<WifiClient>? = null,
+ localOnlyClients: List<WifiClient>? = null
): List<TetheredClient> {
- assertTrue(tracker.updateConnectedClients(ipServers, wifiClients))
+ assertTrue(tracker.updateConnectedClients(ipServers, wifiClients, localOnlyClients))
return tracker.lastTetheredClients
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index e4263db..c2e1617 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -213,7 +213,8 @@
}
@Override
- PendingIntent createRecheckAlarmIntent() {
+ PendingIntent createRecheckAlarmIntent(final String pkgName) {
+ assertEquals(TEST_PACKAGE_NAME, pkgName);
return mAlarmIntent;
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
index 0d686ed..9e287a0 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/FakeTetheringConfiguration.java
@@ -19,22 +19,26 @@
import android.content.Context;
import android.content.res.Resources;
+import androidx.annotation.NonNull;
+
import com.android.net.module.util.SharedLog;
/** FakeTetheringConfiguration is used to override static method for testing. */
public class FakeTetheringConfiguration extends TetheringConfiguration {
FakeTetheringConfiguration(Context ctx, SharedLog log, int id) {
- super(ctx, log, id);
- }
+ super(ctx, log, id, new Dependencies() {
+ @Override
+ boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, @NonNull String moduleName, boolean defaultEnabled) {
+ return defaultEnabled;
+ }
- @Override
- protected String getDeviceConfigProperty(final String name) {
- return null;
- }
-
- @Override
- protected boolean isFeatureEnabled(Context ctx, String namespace, String featureVersionFlag) {
- return false;
+ @Override
+ boolean getDeviceConfigBoolean(@NonNull String namespace, @NonNull String name,
+ boolean defaultValue) {
+ return defaultValue;
+ }
+ });
}
@Override
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 55d9852..91b092a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -19,6 +19,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -61,6 +63,7 @@
private static final String TEST_IFNAME = "test0";
@Mock private IpServer mHotspotIpServer;
+ @Mock private IpServer mLocalHotspotIpServer;
@Mock private IpServer mUsbIpServer;
@Mock private IpServer mEthernetIpServer;
@Mock private IpServer mWifiP2pIpServer;
@@ -90,6 +93,7 @@
when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET);
when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
+ when(mLocalHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI);
when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P);
}
@@ -104,9 +108,10 @@
mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
}
- private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+ private LinkAddress requestDownstreamAddress(final IpServer ipServer, int scope,
+ boolean useLastAddress) {
final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- ipServer, useLastAddress);
+ ipServer, scope, useLastAddress);
when(ipServer.getAddress()).thenReturn(address);
return address;
}
@@ -115,19 +120,19 @@
public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception {
final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress);
final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
assertNotEquals(hotspotPrefix, bluetoothPrefix);
final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix testDupRequest = asIpPrefix(newAddress);
assertNotEquals(hotspotPrefix, testDupRequest);
assertNotEquals(bluetoothPrefix, testDupRequest);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(usbPrefix, bluetoothPrefix);
assertNotEquals(usbPrefix, hotspotPrefix);
@@ -139,25 +144,28 @@
int fakeSubAddr = 0x2b00; // 43.0.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2d01; // 45.1.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2eff; // 46.255.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2f05; // 47.5.
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
- actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
}
@@ -168,7 +176,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mBluetoothAddress.getAddress().getAddress()));
final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
@@ -177,7 +185,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(hotspotAddress.getAddress().getAddress()));
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix);
assertNotEquals(hotspotPrefix, usbPrefix);
@@ -187,7 +195,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer,
- false /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */);
final IpPrefix etherPrefix = asIpPrefix(etherAddress);
assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix);
assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix);
@@ -200,11 +208,11 @@
final int fakeHotspotSubAddr = 0x2b05; // 43.5
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
@@ -214,10 +222,10 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals(hotspotAddress, newHotspotAddress);
final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals(usbAddress, newUsbAddress);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -257,7 +265,7 @@
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
// - Enable hotspot with prefix 192.168.43.0/24
final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr);
assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
// - test mobile network with null NetworkCapabilities. Ideally this should not happen
@@ -311,21 +319,21 @@
// - Restart hotspot again and its prefix is different previous.
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2);
assertNotEquals(hotspotPrefix, hotspotPrefix2);
mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
// - Usb tethering can be enabled and its prefix is different with conflict one.
final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddr);
assertNotEquals(predefinedPrefix, usbPrefix);
assertNotEquals(hotspotPrefix2, usbPrefix);
// - Disable wifi upstream, then wifi's prefix can be selected again.
mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix ethPrefix = asIpPrefix(ethAddr);
assertEquals(predefinedPrefix, ethPrefix);
}
@@ -335,7 +343,7 @@
final int randomAddress = 0x8605; // 134.5
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
// Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -345,7 +353,7 @@
// Check whether return address is next prefix of 192.168.134.0/24.
final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
new LinkAddress("192.168.149.16/19"), null,
@@ -355,7 +363,7 @@
// The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
new LinkAddress("192.168.129.53/18"), null,
@@ -370,7 +378,7 @@
// The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
new LinkAddress("192.168.188.133/17"), null,
@@ -380,7 +388,7 @@
// Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
// 192.168.134/24 ~ 192.168.255.255/24 is not available.
final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
new LinkAddress("192.168.3.59/21"), null,
@@ -389,7 +397,7 @@
// Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
new LinkAddress("192.168.68.43/21"), null,
@@ -399,7 +407,7 @@
// Update an upstream that does *not* conflict, check whether return the same address
// 192.168.5/24.
final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
new LinkAddress("192.168.10.97/21"), null,
@@ -408,7 +416,7 @@
// Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
new LinkAddress("192.168.0.0/17"), null,
@@ -417,7 +425,7 @@
// Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
}
@@ -426,7 +434,7 @@
final int randomAddress = 0x1f2b2a; // 31.43.42
when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
// Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
@@ -437,7 +445,7 @@
// Check whether return address is next address of prefix 192.168.128.0/17.
final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
new LinkAddress("192.1.2.3/8"), null,
@@ -447,7 +455,7 @@
// Check whether return address is under prefix 172.16.0.0/12.
final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
new LinkAddress("172.28.123.100/14"), null,
@@ -458,12 +466,12 @@
// 172.28.0.0 ~ 172.31.255.255 is not available.
// Check whether return address is next address of prefix 172.16.0.0/14.
final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
// Check whether new downstream is next address of address 172.16.0.42/24.
final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
new LinkAddress("172.16.0.1/24"), null,
@@ -474,7 +482,7 @@
// Check whether return address is next address of prefix 172.16.1.42/24.
final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
new LinkAddress("172.16.0.1/13"), null,
@@ -485,11 +493,11 @@
// Check whether return address is next address of prefix 172.16.0.1/13.
final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
// Check whether return address is next address of prefix 172.24.0.42/24.
final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
new LinkAddress("172.24.0.1/12"), null,
@@ -500,11 +508,11 @@
// Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
// Check whether new downstream is next address of address 10.31.43.42/24.
final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
}
@@ -524,7 +532,7 @@
private void assertReseveredWifiP2pPrefix() throws Exception {
LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress);
assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
@@ -544,8 +552,23 @@
// If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer,
- true /* useLastAddress */);
+ CONNECTIVITY_SCOPE_LOCAL, true /* useLastAddress */);
assertEquals(mLegacyWifiP2pAddress, address);
mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
}
+
+ @Test
+ public void testEnableSapAndLohsConcurrently() throws Exception {
+ // 0x2b05 -> 43.5, 0x8605 -> 134.5
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(0x2b05, 0x8605);
+
+ final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+ CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */);
+ assertEquals("Wrong hotspot prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
+
+ final LinkAddress localHotspotAddress = requestDownstreamAddress(mLocalHotspotIpServer,
+ CONNECTIVITY_SCOPE_LOCAL, true /* useLastAddress */);
+ assertEquals("Wrong local hotspot prefix: ", new LinkAddress("192.168.134.5/24"),
+ localHotspotAddress);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index f662c02..3382af8 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -21,14 +21,13 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
+import static com.android.networkstack.tethering.TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD;
+import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
@@ -39,6 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -49,12 +49,13 @@
import android.content.res.Resources;
import android.os.Build;
import android.os.PersistableBundle;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
+import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -73,8 +74,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
+import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Iterator;
@@ -102,13 +102,13 @@
@Mock private ModuleInfo mMi;
private Context mMockContext;
private boolean mHasTelephonyManager;
- private MockitoSession mMockingSession;
private MockContentResolver mContentResolver;
private final PersistableBundle mCarrierConfig = new PersistableBundle();
+ private final MockDependencies mDeps = new MockDependencies();
private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
- super(ctx, log, id);
+ super(ctx, log, id, mDeps);
}
@Override
@@ -151,19 +151,43 @@
}
}
+ private static class MockDependencies extends TetheringConfiguration.Dependencies {
+ private ArrayMap<String, Boolean> mMockFlags = new ArrayMap<>();
+
+ @Override
+ boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace,
+ @NonNull String name, @NonNull String moduleName, boolean defaultEnabled) {
+ return isMockFlagEnabled(name, defaultEnabled);
+ }
+
+ @Override
+ boolean getDeviceConfigBoolean(@NonNull String namespace, @NonNull String name,
+ boolean defaultValue) {
+ // Flags should use isFeatureEnabled instead of getBoolean; see comments in
+ // DeviceConfigUtils. getBoolean should only be used for the two legacy flags below.
+ assertTrue(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD.equals(name)
+ || TETHER_ENABLE_LEGACY_DHCP_SERVER.equals(name));
+
+ // Use the same mocking strategy as isFeatureEnabled for testing
+ return isMockFlagEnabled(name, defaultValue);
+ }
+
+ private boolean isMockFlagEnabled(@NonNull String name, boolean defaultEnabled) {
+ final Boolean flag = mMockFlags.getOrDefault(name, defaultEnabled);
+ // Value in the map can also be null
+ if (flag != null) return flag;
+ return defaultEnabled;
+ }
+
+ void setFeatureEnabled(@NonNull String flag, Boolean enabled) {
+ mMockFlags.put(flag, enabled);
+ }
+ }
+
@Before
public void setUp() throws Exception {
- // TODO: use a dependencies class instead of mock statics.
- mMockingSession = mockitoSession()
- .initMocks(this)
- .mockStatic(DeviceConfig.class)
- .strictness(Strictness.WARN)
- .startMocking();
- DeviceConfigUtils.resetPackageVersionCacheForTest();
- doReturn(null).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
- setTetherForceUpstreamAutomaticFlagVersion(null);
+ MockitoAnnotations.initMocks(this);
+ setTetherForceUpstreamAutomaticFlagEnabled(null);
final PackageInfo pi = new PackageInfo();
pi.setLongVersionCode(TEST_PACKAGE_VERSION);
@@ -202,7 +226,6 @@
@After
public void tearDown() throws Exception {
- mMockingSession.finishMocking();
DeviceConfigUtils.resetPackageVersionCacheForTest();
// Call {@link #clearSettingsProvider()} before and after using FakeSettingsProvider.
FakeSettingsProvider.clearSettingsProvider();
@@ -211,7 +234,7 @@
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
legacyTetherUpstreamTypes);
- return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
}
@Test
@@ -297,7 +320,7 @@
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -320,7 +343,7 @@
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -338,7 +361,7 @@
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
@@ -350,27 +373,26 @@
}
private void initializeBpfOffloadConfiguration(
- final boolean fromRes, final String fromDevConfig) {
+ final boolean fromRes, final Boolean fromDevConfig) {
when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes);
- doReturn(fromDevConfig).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD)));
+ mDeps.setFeatureEnabled(
+ TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, fromDevConfig);
}
@Test
public void testBpfOffloadEnabledByResource() {
initializeBpfOffloadConfiguration(true, null /* unset */);
final TetheringConfiguration enableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(enableByRes.isBpfOffloadEnabled());
}
@Test
public void testBpfOffloadEnabledByDeviceConfigOverride() {
for (boolean res : new boolean[]{true, false}) {
- initializeBpfOffloadConfiguration(res, "true");
+ initializeBpfOffloadConfiguration(res, true);
final TetheringConfiguration enableByDevConOverride =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(enableByDevConOverride.isBpfOffloadEnabled());
}
}
@@ -379,16 +401,16 @@
public void testBpfOffloadDisabledByResource() {
initializeBpfOffloadConfiguration(false, null /* unset */);
final TetheringConfiguration disableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertFalse(disableByRes.isBpfOffloadEnabled());
}
@Test
public void testBpfOffloadDisabledByDeviceConfigOverride() {
for (boolean res : new boolean[]{true, false}) {
- initializeBpfOffloadConfiguration(res, "false");
+ initializeBpfOffloadConfiguration(res, false);
final TetheringConfiguration disableByDevConOverride =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertFalse(disableByDevConOverride.isBpfOffloadEnabled());
}
}
@@ -397,22 +419,18 @@
public void testNewDhcpServerDisabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
true);
- doReturn("false").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
+ mDeps.setFeatureEnabled(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER, false);
final TetheringConfiguration enableByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(enableByRes.useLegacyDhcpServer());
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn("true").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
+ mDeps.setFeatureEnabled(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER, true);
final TetheringConfiguration enableByDevConfig =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(enableByDevConfig.useLegacyDhcpServer());
}
@@ -420,12 +438,10 @@
public void testNewDhcpServerEnabled() {
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
- doReturn("false").when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
+ mDeps.setFeatureEnabled(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER, false);
final TetheringConfiguration cfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertFalse(cfg.useLegacyDhcpServer());
}
@@ -433,7 +449,7 @@
@Test
public void testOffloadIntervalByResource() {
final TetheringConfiguration intervalByDefault =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
intervalByDefault.getOffloadPollInterval());
@@ -442,7 +458,7 @@
when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
override);
final TetheringConfiguration overrideByRes =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(override, overrideByRes.getOffloadPollInterval());
}
}
@@ -451,7 +467,7 @@
public void testGetResourcesBySubId() {
setUpResourceForSubId();
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(cfg.provisioningApp.length == 0);
final int anyValidSubId = 1;
final MockTetheringConfiguration mockCfg =
@@ -493,7 +509,7 @@
mockService(Context.CARRIER_CONFIG_SERVICE,
CarrierConfigManager.class, null);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(cfg.isCarrierSupportTethering);
assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
@@ -506,7 +522,7 @@
CarrierConfigManager.class, mCarrierConfigManager);
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(cfg.isCarrierSupportTethering);
assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
@@ -521,7 +537,7 @@
mCarrierConfig.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, false);
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
if (SdkLevel.isAtLeastT()) {
assertFalse(cfg.isCarrierSupportTethering);
@@ -535,13 +551,13 @@
@Test
public void testEnableLegacyWifiP2PAddress() throws Exception {
final TetheringConfiguration defaultCfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp());
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip))
.thenReturn(true);
final TetheringConfiguration testCfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp());
}
@@ -576,16 +592,13 @@
public void testChooseUpstreamAutomatically_FlagOverride() throws Exception {
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
.thenReturn(false);
- setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
- assertTrue(DeviceConfigUtils.isFeatureEnabled(mMockContext, NAMESPACE_CONNECTIVITY,
- TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION, APEX_NAME, false));
-
+ setTetherForceUpstreamAutomaticFlagEnabled(true);
assertChooseUpstreamAutomaticallyIs(true);
- setTetherForceUpstreamAutomaticFlagVersion(0L);
+ setTetherForceUpstreamAutomaticFlagEnabled(null);
assertChooseUpstreamAutomaticallyIs(false);
- setTetherForceUpstreamAutomaticFlagVersion(Long.MAX_VALUE);
+ setTetherForceUpstreamAutomaticFlagEnabled(false);
assertChooseUpstreamAutomaticallyIs(false);
}
@@ -593,7 +606,7 @@
public void testChooseUpstreamAutomatically_FlagOverrideOnSAndT() throws Exception {
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
.thenReturn(false);
- setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
+ setTetherForceUpstreamAutomaticFlagEnabled(true);
assertChooseUpstreamAutomaticallyIs(false);
}
@@ -604,28 +617,24 @@
// TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION is.
when(mResources.getBoolean(R.bool.config_tether_upstream_automatic))
.thenReturn(false);
- setTetherForceUpstreamAutomaticFlagVersion(TEST_PACKAGE_VERSION - 1);
- assertTrue(DeviceConfigUtils.isFeatureEnabled(mMockContext, NAMESPACE_CONNECTIVITY,
- TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION, APEX_NAME, false));
-
+ setTetherForceUpstreamAutomaticFlagEnabled(true);
assertChooseUpstreamAutomaticallyIs(true);
- setTetherForceUpstreamAutomaticFlagVersion(0L);
+ setTetherForceUpstreamAutomaticFlagEnabled(null);
assertChooseUpstreamAutomaticallyIs(true);
- setTetherForceUpstreamAutomaticFlagVersion(Long.MAX_VALUE);
+ setTetherForceUpstreamAutomaticFlagEnabled(false);
assertChooseUpstreamAutomaticallyIs(true);
}
- private void setTetherForceUpstreamAutomaticFlagVersion(Long version) {
- doReturn(version == null ? null : Long.toString(version)).when(
- () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
- eq(TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION)));
+ private void setTetherForceUpstreamAutomaticFlagEnabled(Boolean enabled) {
+ mDeps.setFeatureEnabled(
+ TetheringConfiguration.TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION, enabled);
}
private void assertChooseUpstreamAutomaticallyIs(boolean value) {
- assertEquals(value, new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)
- .chooseUpstreamAutomatically);
+ assertEquals(value, new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps).chooseUpstreamAutomatically);
}
@Test
@@ -654,7 +663,7 @@
private void assertIsUsingNcm(boolean expected) {
final TetheringConfiguration cfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(expected, cfg.isUsingNcm());
}
@@ -704,7 +713,7 @@
private void assertUsbAndNcmRegexs(final String[] usbRegexs, final String[] ncmRegexs) {
final TetheringConfiguration cfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertArrayEquals(usbRegexs, cfg.tetherableUsbRegexs);
assertArrayEquals(ncmRegexs, cfg.tetherableNcmRegexs);
}
@@ -716,28 +725,28 @@
final int defaultSubnetPrefixLength = 0;
final TetheringConfiguration defaultCfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(defaultSubnetPrefixLength, defaultCfg.getP2pLeasesSubnetPrefixLength());
final int prefixLengthTooSmall = -1;
when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
prefixLengthTooSmall);
final TetheringConfiguration tooSmallCfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(defaultSubnetPrefixLength, tooSmallCfg.getP2pLeasesSubnetPrefixLength());
final int prefixLengthTooLarge = 31;
when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
prefixLengthTooLarge);
final TetheringConfiguration tooLargeCfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(defaultSubnetPrefixLength, tooLargeCfg.getP2pLeasesSubnetPrefixLength());
final int p2pLeasesSubnetPrefixLength = 27;
when(mResources.getInteger(R.integer.config_p2p_leases_subnet_prefix_length)).thenReturn(
p2pLeasesSubnetPrefixLength);
final TetheringConfiguration p2pCfg =
- new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
assertEquals(p2pLeasesSubnetPrefixLength, p2pCfg.getP2pLeasesSubnetPrefixLength());
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index c15b85e..bd8b325 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2988,9 +2988,9 @@
final MacAddress testMac1 = MacAddress.fromString("11:11:11:11:11:11");
final DhcpLeaseParcelable p2pLease = createDhcpLeaseParcelable("clientId1", testMac1,
"192.168.50.24", 24, Long.MAX_VALUE, "test1");
- final List<TetheredClient> p2pClients = notifyDhcpLeasesChanged(TETHERING_WIFI_P2P,
+ final List<TetheredClient> connectedClients = notifyDhcpLeasesChanged(TETHERING_WIFI_P2P,
eventCallbacks, p2pLease);
- callback.expectTetheredClientChanged(p2pClients);
+ callback.expectTetheredClientChanged(connectedClients);
reset(mDhcpServer);
// Run wifi tethering.
@@ -2999,21 +2999,11 @@
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), dhcpEventCbsCaptor.capture());
eventCallbacks = dhcpEventCbsCaptor.getValue();
- // Update mac address from softAp callback before getting dhcp lease.
final MacAddress testMac2 = MacAddress.fromString("22:22:22:22:22:22");
- final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac2,
- false /* isLocalOnly */);
- final List<TetheredClient> p2pAndNoAddrClients = new ArrayList<>(p2pClients);
- p2pAndNoAddrClients.add(noAddrClient);
- callback.expectTetheredClientChanged(p2pAndNoAddrClients);
-
- // Update dhcp lease for wifi tethering.
final DhcpLeaseParcelable wifiLease = createDhcpLeaseParcelable("clientId2", testMac2,
"192.168.43.24", 24, Long.MAX_VALUE, "test2");
- final List<TetheredClient> p2pAndWifiClients = new ArrayList<>(p2pClients);
- p2pAndWifiClients.addAll(notifyDhcpLeasesChanged(TETHERING_WIFI,
- eventCallbacks, wifiLease));
- callback.expectTetheredClientChanged(p2pAndWifiClients);
+ verifyHotspotClientUpdate(false /* isLocalOnly */, testMac2, wifiLease, connectedClients,
+ eventCallbacks, callback);
// Test onStarted callback that register second callback when tethering is running.
TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
@@ -3021,7 +3011,7 @@
mTethering.registerTetheringEventCallback(callback2);
mLooper.dispatchAll();
});
- callback2.expectTetheredClientChanged(p2pAndWifiClients);
+ callback2.expectTetheredClientChanged(connectedClients);
}
@Test
@@ -3043,26 +3033,34 @@
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
any(), dhcpEventCbsCaptor.capture());
final IDhcpEventCallbacks eventCallbacks = dhcpEventCbsCaptor.getValue();
- // Update mac address from softAp callback before getting dhcp lease.
- final MacAddress testMac = MacAddress.fromString("22:22:22:22:22:22");
- final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac,
- true /* isLocalOnly */);
- final List<TetheredClient> noAddrLocalOnlyClients = new ArrayList<>();
- noAddrLocalOnlyClients.add(noAddrClient);
- callback.expectTetheredClientChanged(noAddrLocalOnlyClients);
- // Update dhcp lease for local only hotspot.
+ final List<TetheredClient> connectedClients = new ArrayList<>();
+ final MacAddress testMac = MacAddress.fromString("22:22:22:22:22:22");
final DhcpLeaseParcelable wifiLease = createDhcpLeaseParcelable("clientId", testMac,
"192.168.43.24", 24, Long.MAX_VALUE, "test");
- final List<TetheredClient> localOnlyClients = notifyDhcpLeasesChanged(TETHERING_WIFI,
- eventCallbacks, wifiLease);
- callback.expectTetheredClientChanged(localOnlyClients);
+ verifyHotspotClientUpdate(true /* isLocalOnly */, testMac, wifiLease, connectedClients,
+ eventCallbacks, callback);
// Client disconnect from local only hotspot.
mLocalOnlyHotspotCallback.onConnectedClientsChanged(Collections.emptyList());
callback.expectTetheredClientChanged(Collections.emptyList());
}
+ private void verifyHotspotClientUpdate(final boolean isLocalOnly, final MacAddress testMac,
+ final DhcpLeaseParcelable dhcpLease, final List<TetheredClient> currentClients,
+ final IDhcpEventCallbacks dhcpCallback, final TestTetheringEventCallback callback)
+ throws Exception {
+ // Update mac address from softAp callback before getting dhcp lease.
+ final TetheredClient noAddrClient = notifyConnectedWifiClientsChanged(testMac, isLocalOnly);
+ final List<TetheredClient> withNoAddrClients = new ArrayList<>(currentClients);
+ withNoAddrClients.add(noAddrClient);
+ callback.expectTetheredClientChanged(withNoAddrClients);
+
+ // Update dhcp lease for hotspot.
+ currentClients.addAll(notifyDhcpLeasesChanged(TETHERING_WIFI, dhcpCallback, dhcpLease));
+ callback.expectTetheredClientChanged(currentClients);
+ }
+
private TetheredClient notifyConnectedWifiClientsChanged(final MacAddress mac,
boolean isLocalOnly) throws Exception {
final ArrayList<WifiClient> wifiClients = new ArrayList<>();
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index 77950ac..e2c924c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -150,10 +150,13 @@
}
private void addUpstreamEvent(UpstreamEvents.Builder upstreamEvents,
- final UpstreamType expectedResult, final long duration) {
+ final UpstreamType expectedResult, final long duration, final long txBytes,
+ final long rxBytes) {
UpstreamEvent.Builder upstreamEvent = UpstreamEvent.newBuilder()
.setUpstreamType(expectedResult)
- .setDurationMillis(duration);
+ .setDurationMillis(duration)
+ .setTxBytes(txBytes)
+ .setRxBytes(rxBytes);
upstreamEvents.addUpstreamEvent(upstreamEvent);
}
@@ -164,7 +167,7 @@
incrementCurrentTime(duration);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
// Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration, 0L, 0L);
updateErrorAndSendReport(type, TETHER_ERROR_NO_ERROR);
verifyReport(expectedResult, ErrorCode.EC_NO_ERROR, UserType.USER_UNKNOWN,
@@ -193,7 +196,7 @@
updateErrorAndSendReport(TETHERING_WIFI, errorCode);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN,
upstreamEvents, getElapsedRealtime());
reset(mTetheringMetrics);
@@ -235,7 +238,7 @@
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
// Set UpstreamType as NO_NETWORK because the upstream type has not been changed.
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_NO_NETWORK, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR, expectedResult,
upstreamEvents, getElapsedRealtime());
reset(mTetheringMetrics);
@@ -260,7 +263,7 @@
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(upstreamEvents, expectedResult, duration);
+ addUpstreamEvent(upstreamEvents, expectedResult, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_UNKNOWN, upstreamEvents, getElapsedRealtime());
reset(mTetheringMetrics);
@@ -296,7 +299,7 @@
UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
- currentTimeMillis() - wifiTetheringStartTime);
+ currentTimeMillis() - wifiTetheringStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_DHCPSERVER_ERROR,
UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
currentTimeMillis() - wifiTetheringStartTime);
@@ -305,7 +308,7 @@
UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
- currentTimeMillis() - usbTetheringStartTime);
+ currentTimeMillis() - usbTetheringStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_ENABLE_FORWARDING_ERROR,
UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
@@ -315,7 +318,7 @@
UpstreamEvents.Builder bluetoothTetheringUpstreamEvents = UpstreamEvents.newBuilder();
addUpstreamEvent(bluetoothTetheringUpstreamEvents, UpstreamType.UT_NO_NETWORK,
- currentTimeMillis() - bluetoothTetheringStartTime);
+ currentTimeMillis() - bluetoothTetheringStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_BLUETOOTH, ErrorCode.EC_TETHER_IFACE_ERROR,
UserType.USER_GMS, bluetoothTetheringUpstreamEvents,
currentTimeMillis() - bluetoothTetheringStartTime);
@@ -336,7 +339,7 @@
UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_WIFI,
- currentTimeMillis() - usbTetheringStartTime);
+ currentTimeMillis() - usbTetheringStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_NO_ERROR,
UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
currentTimeMillis() - usbTetheringStartTime);
@@ -345,7 +348,7 @@
UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_WIFI,
- currentTimeMillis() - wifiUpstreamStartTime);
+ currentTimeMillis() - wifiUpstreamStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
currentTimeMillis() - wifiTetheringStartTime);
@@ -368,9 +371,9 @@
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, wifiDuration);
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_BLUETOOTH, bluetoothDuration);
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_CELLULAR, celltoothDuration);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, wifiDuration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_BLUETOOTH, bluetoothDuration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UpstreamType.UT_CELLULAR, celltoothDuration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_SETTINGS, upstreamEvents,
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 229dce3..b3f8ed6 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -103,18 +103,6 @@
}
bpf {
- name: "offload@inprocess.o",
- srcs: ["offload@inprocess.c"],
- btf: true,
- cflags: [
- "-Wall",
- "-Werror",
- "-DBTF",
- "-DINPROCESS",
- ],
-}
-
-bpf {
name: "test.o",
srcs: ["test.c"],
cflags: [
@@ -135,18 +123,6 @@
}
bpf {
- name: "test@inprocess.o",
- srcs: ["test@inprocess.c"],
- btf: true,
- cflags: [
- "-Wall",
- "-Werror",
- "-DBTF",
- "-DINPROCESS",
- ],
-}
-
-bpf {
name: "clatd.o",
srcs: ["clatd.c"],
btf: true,
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index f2a3e62..3797a38 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -19,8 +19,8 @@
#include <netinet/in.h>
#include <stdint.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
diff --git a/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
index b7ca3af..ed33cc9 100644
--- a/bpf_progs/bpf_net_helpers.h
+++ b/bpf_progs/bpf_net_helpers.h
@@ -86,3 +86,30 @@
if (len > skb->len) len = skb->len;
if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len);
}
+
+// constants for passing in to 'bool egress'
+static const bool INGRESS = false;
+static const bool EGRESS = true;
+
+// constants for passing in to 'bool downstream'
+static const bool UPSTREAM = false;
+static const bool DOWNSTREAM = true;
+
+// constants for passing in to 'bool is_ethernet'
+static const bool RAWIP = false;
+static const bool ETHER = true;
+
+// constants for passing in to 'bool updatetime'
+static const bool NO_UPDATETIME = false;
+static const bool UPDATETIME = true;
+
+// constants for passing in to ignore_on_eng / ignore_on_user / ignore_on_userdebug
+// define's instead of static const due to tm-mainline-prod compiler static_assert limitations
+#define LOAD_ON_ENG false
+#define LOAD_ON_USER false
+#define LOAD_ON_USERDEBUG false
+#define IGNORE_ON_ENG true
+#define IGNORE_ON_USER true
+#define IGNORE_ON_USERDEBUG true
+
+#define KVER_4_14 KVER(4, 14, 0)
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 7c6811a..85ba58e 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -30,8 +30,8 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
@@ -52,12 +52,6 @@
__be32 identification;
};
-// constants for passing in to 'bool is_ethernet'
-static const bool RAWIP = false;
-static const bool ETHER = true;
-
-#define KVER_4_14 KVER(4, 14, 0)
-
DEFINE_BPF_MAP_GRW(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16, AID_SYSTEM)
static inline __always_inline int nat64(struct __sk_buff* skb,
diff --git a/bpf_progs/dscpPolicy.c b/bpf_progs/dscpPolicy.c
index 72f63c6..262b65b 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -27,8 +27,8 @@
#include <stdint.h>
#include <string.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
#include "dscpPolicy.h"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index ce3315b..245ad7a 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-// The resulting .o needs to load on the Android T Beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include <bpf_helpers.h>
#include <linux/bpf.h>
@@ -42,10 +42,6 @@
static const int BPF_NOMATCH = 0;
static const int BPF_MATCH = 1;
-// Used for 'bool egress'
-static const bool INGRESS = false;
-static const bool EGRESS = true;
-
// Used for 'bool enable_tracing'
static const bool TRACE_ON = true;
static const bool TRACE_OFF = false;
@@ -64,15 +60,15 @@
#define DEFINE_BPF_MAP_NO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
AID_ROOT, AID_NET_BW_ACCT, 0060, "fs_bpf_net_shared", "", false, \
- BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, /*ignore_on_eng*/false, \
- /*ignore_on_user*/false, /*ignore_on_userdebug*/false)
+ BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, LOAD_ON_ENG, \
+ LOAD_ON_USER, LOAD_ON_USERDEBUG)
// For maps netd only needs read only access to
#define DEFINE_BPF_MAP_RO_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
DEFINE_BPF_MAP_EXT(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries, \
AID_ROOT, AID_NET_BW_ACCT, 0460, "fs_bpf_netd_readonly", "", false, \
- BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, /*ignore_on_eng*/false, \
- /*ignore_on_user*/false, /*ignore_on_userdebug*/false)
+ BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, LOAD_ON_ENG, \
+ LOAD_ON_USER, LOAD_ON_USERDEBUG)
// For maps netd needs to be able to read and write
#define DEFINE_BPF_MAP_RW_NETD(the_map, TYPE, TypeOfKey, TypeOfValue, num_entries) \
@@ -103,15 +99,15 @@
// A single-element configuration array, packet tracing is enabled when 'true'.
DEFINE_BPF_MAP_EXT(packet_trace_enabled_map, ARRAY, uint32_t, bool, 1,
AID_ROOT, AID_SYSTEM, 0060, "fs_bpf_net_shared", "", false,
- BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, /*ignore_on_eng*/false,
- /*ignore_on_user*/true, /*ignore_on_userdebug*/false)
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG,
+ IGNORE_ON_USER, LOAD_ON_USERDEBUG)
// A ring buffer on which packet information is pushed. This map will only be loaded
// on eng and userdebug devices. User devices won't load this to save memory.
DEFINE_BPF_RINGBUF_EXT(packet_trace_ringbuf, PacketTrace, PACKET_TRACE_BUF_SIZE,
AID_ROOT, AID_SYSTEM, 0060, "fs_bpf_net_shared", "", false,
- BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, /*ignore_on_eng*/false,
- /*ignore_on_user*/true, /*ignore_on_userdebug*/false);
+ BPFLOADER_IGNORED_ON_VERSION, BPFLOADER_MAX_VER, LOAD_ON_ENG,
+ IGNORE_ON_USER, LOAD_ON_USERDEBUG);
// iptables xt_bpf programs need to be usable by both netd and netutils_wrappers
// selinux contexts, because even non-xt_bpf iptables mutations are implemented as
@@ -176,36 +172,38 @@
* Especially since the number of packets is important for any future clat offload correction.
* (which adjusts upward by 20 bytes per packet to account for ipv4 -> ipv6 header conversion)
*/
-#define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey) \
- static __always_inline inline void update_##the_stats_map(struct __sk_buff* skb, \
- bool egress, TypeOfKey* key) { \
- StatsValue* value = bpf_##the_stats_map##_lookup_elem(key); \
- if (!value) { \
- StatsValue newValue = {}; \
- bpf_##the_stats_map##_update_elem(key, &newValue, BPF_NOEXIST); \
- value = bpf_##the_stats_map##_lookup_elem(key); \
- } \
- if (value) { \
- const int mtu = 1500; \
- uint64_t packets = 1; \
- uint64_t bytes = skb->len; \
- if (bytes > mtu) { \
- bool is_ipv6 = (skb->protocol == htons(ETH_P_IPV6)); \
- int ip_overhead = (is_ipv6 ? sizeof(struct ipv6hdr) : sizeof(struct iphdr)); \
- int tcp_overhead = ip_overhead + sizeof(struct tcphdr) + 12; \
- int mss = mtu - tcp_overhead; \
- uint64_t payload = bytes - tcp_overhead; \
- packets = (payload + mss - 1) / mss; \
- bytes = tcp_overhead * packets + payload; \
- } \
- if (egress) { \
- __sync_fetch_and_add(&value->txPackets, packets); \
- __sync_fetch_and_add(&value->txBytes, bytes); \
- } else { \
- __sync_fetch_and_add(&value->rxPackets, packets); \
- __sync_fetch_and_add(&value->rxBytes, bytes); \
- } \
- } \
+#define DEFINE_UPDATE_STATS(the_stats_map, TypeOfKey) \
+ static __always_inline inline void update_##the_stats_map(const struct __sk_buff* const skb, \
+ const TypeOfKey* const key, \
+ const bool egress, \
+ const unsigned kver) { \
+ StatsValue* value = bpf_##the_stats_map##_lookup_elem(key); \
+ if (!value) { \
+ StatsValue newValue = {}; \
+ bpf_##the_stats_map##_update_elem(key, &newValue, BPF_NOEXIST); \
+ value = bpf_##the_stats_map##_lookup_elem(key); \
+ } \
+ if (value) { \
+ const int mtu = 1500; \
+ uint64_t packets = 1; \
+ uint64_t bytes = skb->len; \
+ if (bytes > mtu) { \
+ bool is_ipv6 = (skb->protocol == htons(ETH_P_IPV6)); \
+ int ip_overhead = (is_ipv6 ? sizeof(struct ipv6hdr) : sizeof(struct iphdr)); \
+ int tcp_overhead = ip_overhead + sizeof(struct tcphdr) + 12; \
+ int mss = mtu - tcp_overhead; \
+ uint64_t payload = bytes - tcp_overhead; \
+ packets = (payload + mss - 1) / mss; \
+ bytes = tcp_overhead * packets + payload; \
+ } \
+ if (egress) { \
+ __sync_fetch_and_add(&value->txPackets, packets); \
+ __sync_fetch_and_add(&value->txBytes, bytes); \
+ } else { \
+ __sync_fetch_and_add(&value->rxPackets, packets); \
+ __sync_fetch_and_add(&value->rxBytes, bytes); \
+ } \
+ } \
}
DEFINE_UPDATE_STATS(app_uid_stats_map, uint32_t)
@@ -385,12 +383,15 @@
return PASS;
}
-static __always_inline inline void update_stats_with_config(struct __sk_buff* skb, bool egress,
- StatsKey* key, uint32_t selectedMap) {
+static __always_inline inline void update_stats_with_config(const uint32_t selectedMap,
+ const struct __sk_buff* const skb,
+ const StatsKey* const key,
+ const bool egress,
+ const unsigned kver) {
if (selectedMap == SELECT_MAP_A) {
- update_stats_map_A(skb, egress, key);
+ update_stats_map_A(skb, key, egress, kver);
} else {
- update_stats_map_B(skb, egress, key);
+ update_stats_map_B(skb, key, egress, kver);
}
}
@@ -411,10 +412,8 @@
// Always allow and never count clat traffic. Only the IPv4 traffic on the stacked
// interface is accounted for and subject to usage restrictions.
- // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
- if (sock_uid == AID_CLAT || uid == AID_CLAT) {
- return PASS;
- }
+ // CLAT IPv6 TX sockets are *always* tagged with CLAT uid, see tagSocketAsClat()
+ if (uid == AID_CLAT) return PASS;
int match = bpf_owner_match(skb, sock_uid, egress, kver);
@@ -440,17 +439,17 @@
uint32_t mapSettingKey = CURRENT_STATS_MAP_CONFIGURATION_KEY;
uint32_t* selectedMap = bpf_configuration_map_lookup_elem(&mapSettingKey);
- // Use asm("%0 &= 1" : "+r"(match)) before return match,
- // to help kernel's bpf verifier, so that it can be 100% certain
- // that the returned value is always BPF_NOMATCH(0) or BPF_MATCH(1).
- if (!selectedMap) {
- asm("%0 &= 1" : "+r"(match));
- return match;
- }
+ if (!selectedMap) return PASS; // cannot happen, needed to keep bpf verifier happy
do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
- update_stats_with_config(skb, egress, &key, *selectedMap);
- update_app_uid_stats_map(skb, egress, &uid);
+ update_stats_with_config(*selectedMap, skb, &key, egress, kver);
+ update_app_uid_stats_map(skb, &uid, egress, kver);
+
+ // We've already handled DROP_UNLESS_DNS up above, thus when we reach here the only
+ // possible values of match are DROP(0) or PASS(1), however we need to use
+ // "match &= 1" before 'return match' to help the kernel's bpf verifier,
+ // so that it can be 100% certain that the returned value is always 0 or 1.
+ // We use assembly so that it cannot be optimized out by a too smart compiler.
asm("%0 &= 1" : "+r"(match));
return match;
}
@@ -501,9 +500,8 @@
// Clat daemon does not generate new traffic, all its traffic is accounted for already
// on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
// but that can be corrected for later when merging v4-foo stats into interface foo's).
- // TODO: remove sock_uid check once Nat464Xlat javaland adds the socket tag AID_CLAT for clat.
+ // CLAT sockets are created by system server and tagged as uid CLAT, see tagSocketAsClat()
uint32_t sock_uid = bpf_get_socket_uid(skb);
- if (sock_uid == AID_CLAT) return BPF_NOMATCH;
if (sock_uid == AID_SYSTEM) {
uint64_t cookie = bpf_get_socket_cookie(skb);
UidTagValue* utag = bpf_cookie_tag_map_lookup_elem(&cookie);
@@ -511,7 +509,7 @@
}
uint32_t key = skb->ifindex;
- update_iface_stats_map(skb, EGRESS, &key);
+ update_iface_stats_map(skb, &key, EGRESS, KVER_NONE);
return BPF_MATCH;
}
@@ -524,7 +522,7 @@
// Keep that in mind when moving this out of iptables xt_bpf and into tc ingress (or xdp).
uint32_t key = skb->ifindex;
- update_iface_stats_map(skb, INGRESS, &key);
+ update_iface_stats_map(skb, &key, INGRESS, KVER_NONE);
return BPF_MATCH;
}
@@ -534,7 +532,7 @@
if (is_received_skb(skb)) {
// Account for ingress traffic before tc drops it.
uint32_t key = skb->ifindex;
- update_iface_stats_map(skb, INGRESS, &key);
+ update_iface_stats_map(skb, &key, INGRESS, KVER_NONE);
}
return TC_ACT_UNSPEC;
}
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 56ace19..80d1a41 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -38,13 +38,7 @@
// Warning: values other than AID_ROOT don't work for map uid on BpfLoader < v0.21
#define TETHERING_UID AID_ROOT
-#ifdef INPROCESS
-#define DEFAULT_BPF_MAP_SELINUX_CONTEXT "fs_bpf_net_shared"
-#define DEFAULT_BPF_PROG_SELINUX_CONTEXT "fs_bpf_net_shared"
-#define TETHERING_GID AID_SYSTEM
-#else
#define TETHERING_GID AID_NETWORK_STACK
-#endif
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
@@ -131,7 +125,7 @@
TETHERING_GID)
static inline __always_inline int do_forward6(struct __sk_buff* skb, const bool is_ethernet,
- const bool downstream) {
+ const bool downstream, const unsigned kver) {
// Must be meta-ethernet IPv6 frame
if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_PIPE;
@@ -305,13 +299,13 @@
DEFINE_BPF_PROG("schedcls/tether_downstream6_ether", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream6_ether)
(struct __sk_buff* skb) {
- return do_forward6(skb, /* is_ethernet */ true, /* downstream */ true);
+ return do_forward6(skb, ETHER, DOWNSTREAM, KVER_NONE);
}
DEFINE_BPF_PROG("schedcls/tether_upstream6_ether", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream6_ether)
(struct __sk_buff* skb) {
- return do_forward6(skb, /* is_ethernet */ true, /* downstream */ false);
+ return do_forward6(skb, ETHER, UPSTREAM, KVER_NONE);
}
// Note: section names must be unique to prevent programs from appending to each other,
@@ -331,13 +325,13 @@
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_rawip$4_14", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream6_rawip_4_14, KVER(4, 14, 0))
(struct __sk_buff* skb) {
- return do_forward6(skb, /* is_ethernet */ false, /* downstream */ true);
+ return do_forward6(skb, RAWIP, DOWNSTREAM, KVER(4, 14, 0));
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_rawip$4_14", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream6_rawip_4_14, KVER(4, 14, 0))
(struct __sk_buff* skb) {
- return do_forward6(skb, /* is_ethernet */ false, /* downstream */ false);
+ return do_forward6(skb, RAWIP, UPSTREAM, KVER(4, 14, 0));
}
// and define no-op stubs for pre-4.14 kernels.
@@ -362,7 +356,8 @@
static inline __always_inline int do_forward4_bottom(struct __sk_buff* skb,
const int l2_header_size, void* data, const void* data_end,
struct ethhdr* eth, struct iphdr* ip, const bool is_ethernet,
- const bool downstream, const bool updatetime, const bool is_tcp) {
+ const bool downstream, const bool updatetime, const bool is_tcp,
+ const unsigned kver) {
struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL;
struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);
@@ -552,7 +547,7 @@
}
static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool is_ethernet,
- const bool downstream, const bool updatetime) {
+ const bool downstream, const bool updatetime, const unsigned kver) {
// Require ethernet dst mac address to be our unicast address.
if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
@@ -640,10 +635,10 @@
// if the underlying requisite kernel support (bpf_ktime_get_boot_ns) was backported.
if (is_tcp) {
return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
- is_ethernet, downstream, updatetime, /* is_tcp */ true);
+ is_ethernet, downstream, updatetime, /* is_tcp */ true, kver);
} else {
return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
- is_ethernet, downstream, updatetime, /* is_tcp */ false);
+ is_ethernet, downstream, updatetime, /* is_tcp */ false, kver);
}
}
@@ -652,25 +647,25 @@
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_rawip$5_8", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_rawip_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ true);
+ return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER(5, 8, 0));
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_rawip$5_8", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_rawip_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ true);
+ return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER(5, 8, 0));
}
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_ether$5_8", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_ether_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ true);
+ return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER(5, 8, 0));
}
DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_ether$5_8", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_ether_5_8, KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ true);
+ return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER(5, 8, 0));
}
// Full featured (optional) implementations for 4.14-S, 4.19-S & 5.4-S kernels
@@ -681,7 +676,7 @@
sched_cls_tether_downstream4_rawip_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ true);
+ return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER(4, 14, 0));
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$opt",
@@ -689,7 +684,7 @@
sched_cls_tether_upstream4_rawip_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ true);
+ return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER(4, 14, 0));
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$opt",
@@ -697,7 +692,7 @@
sched_cls_tether_downstream4_ether_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ true);
+ return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER(4, 14, 0));
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$opt",
@@ -705,7 +700,7 @@
sched_cls_tether_upstream4_ether_opt,
KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ true);
+ return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER(4, 14, 0));
}
// Partial (TCP-only: will not update 'last_used' field) implementations for 4.14+ kernels.
@@ -725,13 +720,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$5_4", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_rawip_5_4, KVER(5, 4, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ false);
+ return do_forward4(skb, RAWIP, DOWNSTREAM, NO_UPDATETIME, KVER(5, 4, 0));
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$5_4", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_rawip_5_4, KVER(5, 4, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ false);
+ return do_forward4(skb, RAWIP, UPSTREAM, NO_UPDATETIME, KVER(5, 4, 0));
}
// RAWIP: Optional for 4.14/4.19 (R) kernels -- which support bpf_skb_change_head().
@@ -742,7 +737,7 @@
sched_cls_tether_downstream4_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ true, /* updatetime */ false);
+ return do_forward4(skb, RAWIP, DOWNSTREAM, NO_UPDATETIME, KVER(4, 14, 0));
}
DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$4_14",
@@ -750,7 +745,7 @@
sched_cls_tether_upstream4_rawip_4_14,
KVER(4, 14, 0), KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ false, /* downstream */ false, /* updatetime */ false);
+ return do_forward4(skb, RAWIP, UPSTREAM, NO_UPDATETIME, KVER(4, 14, 0));
}
// ETHER: Required for 4.14-Q/R, 4.19-Q/R & 5.4-R kernels.
@@ -758,13 +753,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$4_14", TETHERING_UID, TETHERING_GID,
sched_cls_tether_downstream4_ether_4_14, KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ true, /* updatetime */ false);
+ return do_forward4(skb, ETHER, DOWNSTREAM, NO_UPDATETIME, KVER(4, 14, 0));
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$4_14", TETHERING_UID, TETHERING_GID,
sched_cls_tether_upstream4_ether_4_14, KVER(4, 14, 0), KVER(5, 8, 0))
(struct __sk_buff* skb) {
- return do_forward4(skb, /* is_ethernet */ true, /* downstream */ false, /* updatetime */ false);
+ return do_forward4(skb, ETHER, UPSTREAM, NO_UPDATETIME, KVER(4, 14, 0));
}
// Placeholder (no-op) implementations for older Q kernels
@@ -820,9 +815,9 @@
if ((void*)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto == htons(ETH_P_IPV6))
- return do_xdp_forward6(ctx, /* is_ethernet */ true, downstream);
+ return do_xdp_forward6(ctx, ETHER, downstream);
if (eth->h_proto == htons(ETH_P_IP))
- return do_xdp_forward4(ctx, /* is_ethernet */ true, downstream);
+ return do_xdp_forward4(ctx, ETHER, downstream);
// Anything else we don't know how to handle...
return XDP_PASS;
@@ -836,8 +831,8 @@
if (data_end - data < 1) return XDP_PASS;
const uint8_t v = (*(uint8_t*)data) >> 4;
- if (v == 6) return do_xdp_forward6(ctx, /* is_ethernet */ false, downstream);
- if (v == 4) return do_xdp_forward4(ctx, /* is_ethernet */ false, downstream);
+ if (v == 6) return do_xdp_forward6(ctx, RAWIP, downstream);
+ if (v == 4) return do_xdp_forward4(ctx, RAWIP, downstream);
// Anything else we don't know how to handle...
return XDP_PASS;
@@ -848,22 +843,22 @@
DEFINE_XDP_PROG("xdp/tether_downstream_ether",
xdp_tether_downstream_ether) {
- return do_xdp_forward_ether(ctx, /* downstream */ true);
+ return do_xdp_forward_ether(ctx, DOWNSTREAM);
}
DEFINE_XDP_PROG("xdp/tether_downstream_rawip",
xdp_tether_downstream_rawip) {
- return do_xdp_forward_rawip(ctx, /* downstream */ true);
+ return do_xdp_forward_rawip(ctx, DOWNSTREAM);
}
DEFINE_XDP_PROG("xdp/tether_upstream_ether",
xdp_tether_upstream_ether) {
- return do_xdp_forward_ether(ctx, /* downstream */ false);
+ return do_xdp_forward_ether(ctx, UPSTREAM);
}
DEFINE_XDP_PROG("xdp/tether_upstream_rawip",
xdp_tether_upstream_rawip) {
- return do_xdp_forward_rawip(ctx, /* downstream */ false);
+ return do_xdp_forward_rawip(ctx, UPSTREAM);
}
LICENSE("Apache 2.0");
diff --git a/bpf_progs/offload@inprocess.c b/bpf_progs/offload@inprocess.c
deleted file mode 120000
index 4092e0d..0000000
--- a/bpf_progs/offload@inprocess.c
+++ /dev/null
@@ -1 +0,0 @@
-offload.c
\ No newline at end of file
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index d1f780f..091743c 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -32,13 +32,7 @@
// Warning: values other than AID_ROOT don't work for map uid on BpfLoader < v0.21
#define TETHERING_UID AID_ROOT
-#ifdef INPROCESS
-#define DEFAULT_BPF_MAP_SELINUX_CONTEXT "fs_bpf_net_shared"
-#define DEFAULT_BPF_PROG_SELINUX_CONTEXT "fs_bpf_net_shared"
-#define TETHERING_GID AID_SYSTEM
-#else
#define TETHERING_GID AID_NETWORK_STACK
-#endif
// This is non production code, only used for testing
// Needed because the bitmap array definition is non-kosher for pre-T OS devices.
diff --git a/bpf_progs/test@inprocess.c b/bpf_progs/test@inprocess.c
deleted file mode 120000
index aeebb26..0000000
--- a/bpf_progs/test@inprocess.c
+++ /dev/null
@@ -1 +0,0 @@
-test.c
\ No newline at end of file
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index d119db6..2930cbd 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -429,6 +429,10 @@
private final DiscoveryListener mWrapped;
private final Executor mWrappedExecutor;
private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>();
+ // When this flag is set to true, no further service found or lost callbacks should be
+ // handled. This flag indicates that the network for this DelegatingDiscoveryListener is
+ // lost, and any further callbacks would be redundant.
+ private boolean mAllServicesLost = false;
private DelegatingDiscoveryListener(Network network, DiscoveryListener listener,
Executor executor) {
@@ -445,6 +449,7 @@
serviceInfo.setNetwork(mNetwork);
mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo));
}
+ mAllServicesLost = true;
}
@Override
@@ -486,12 +491,22 @@
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
+ if (mAllServicesLost) {
+ // This DelegatingDiscoveryListener no longer has a network connection. Ignore
+ // the callback.
+ return;
+ }
mFoundInfo.add(new TrackedNsdInfo(serviceInfo));
mWrappedExecutor.execute(() -> mWrapped.onServiceFound(serviceInfo));
}
@Override
public void onServiceLost(NsdServiceInfo serviceInfo) {
+ if (mAllServicesLost) {
+ // This DelegatingDiscoveryListener no longer has a network connection. Ignore
+ // the callback.
+ return;
+ }
mFoundInfo.remove(new TrackedNsdInfo(serviceInfo));
mWrappedExecutor.execute(() -> mWrapped.onServiceLost(serviceInfo));
}
diff --git a/framework/Android.bp b/framework/Android.bp
index 2d729c5..123f02a 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -105,7 +105,13 @@
java_library {
name: "framework-connectivity-pre-jarjar",
- defaults: ["framework-connectivity-defaults"],
+ defaults: [
+ "framework-connectivity-defaults",
+ ],
+ static_libs: [
+ "cronet_aml_api_java",
+ "cronet_aml_java",
+ ],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
// to generate the connectivity stubs. That would create a circular dependency
@@ -117,9 +123,23 @@
visibility: ["//packages/modules/Connectivity:__subpackages__"]
}
+java_defaults {
+ name: "CronetJavaDefaults",
+ srcs: [":cronet_aml_api_sources"],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
+ impl_only_static_libs: [
+ "cronet_aml_java",
+ ],
+}
+
java_sdk_library {
name: "framework-connectivity",
- defaults: ["framework-connectivity-defaults"],
+ defaults: [
+ "framework-connectivity-defaults",
+ "CronetJavaDefaults",
+ ],
installable: true,
jarjar_rules: ":framework-connectivity-jarjar-rules",
permitted_packages: ["android.net"],
@@ -147,6 +167,7 @@
"//frameworks/opt/net/ethernet/tests:__subpackages__",
"//frameworks/opt/telephony/tests/telephonytests",
"//packages/modules/CaptivePortalLogin/tests",
+ "//packages/modules/Connectivity/Cronet/tests:__subpackages__",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
"//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/IPsec/tests/iketests",
@@ -183,6 +204,7 @@
"libnativehelper",
],
header_libs: [
+ "bpf_headers",
"dnsproxyd_protocol_headers",
],
stl: "none",
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 672e3e2..6860c3c 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -525,3 +525,292 @@
}
+package android.net.http {
+
+ public abstract class BidirectionalStream {
+ ctor public BidirectionalStream();
+ method public abstract void cancel();
+ method public abstract void flush();
+ method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
+ method @NonNull public abstract String getHttpMethod();
+ method public abstract int getPriority();
+ method public abstract int getTrafficStatsTag();
+ method public abstract int getTrafficStatsUid();
+ method public abstract boolean hasTrafficStatsTag();
+ method public abstract boolean hasTrafficStatsUid();
+ method public abstract boolean isDelayRequestHeadersUntilFirstFlushEnabled();
+ method public abstract boolean isDone();
+ method public abstract void read(@NonNull java.nio.ByteBuffer);
+ method public abstract void start();
+ method public abstract void write(@NonNull java.nio.ByteBuffer, boolean);
+ field public static final int STREAM_PRIORITY_HIGHEST = 4; // 0x4
+ field public static final int STREAM_PRIORITY_IDLE = 0; // 0x0
+ field public static final int STREAM_PRIORITY_LOW = 2; // 0x2
+ field public static final int STREAM_PRIORITY_LOWEST = 1; // 0x1
+ field public static final int STREAM_PRIORITY_MEDIUM = 3; // 0x3
+ }
+
+ public abstract static class BidirectionalStream.Builder {
+ ctor public BidirectionalStream.Builder();
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder addHeader(@NonNull String, @NonNull String);
+ method @NonNull public abstract android.net.http.BidirectionalStream build();
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder setDelayRequestHeadersUntilFirstFlushEnabled(boolean);
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder setHttpMethod(@NonNull String);
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder setPriority(int);
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder setTrafficStatsTag(int);
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder setTrafficStatsUid(int);
+ }
+
+ public static interface BidirectionalStream.Callback {
+ method public void onCanceled(@NonNull android.net.http.BidirectionalStream, @Nullable android.net.http.UrlResponseInfo);
+ method public void onFailed(@NonNull android.net.http.BidirectionalStream, @Nullable android.net.http.UrlResponseInfo, @NonNull android.net.http.HttpException);
+ method public void onReadCompleted(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer, boolean);
+ method public void onResponseHeadersReceived(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo);
+ method public void onResponseTrailersReceived(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull android.net.http.HeaderBlock);
+ method public void onStreamReady(@NonNull android.net.http.BidirectionalStream);
+ method public void onSucceeded(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo);
+ method public void onWriteCompleted(@NonNull android.net.http.BidirectionalStream, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer, boolean);
+ }
+
+ public abstract class CallbackException extends android.net.http.HttpException {
+ ctor protected CallbackException(@Nullable String, @Nullable Throwable);
+ }
+
+ public class ConnectionMigrationOptions {
+ method public int getAllowNonDefaultNetworkUsage();
+ method public int getDefaultNetworkMigration();
+ method public int getPathDegradationMigration();
+ field public static final int MIGRATION_OPTION_DISABLED = 2; // 0x2
+ field public static final int MIGRATION_OPTION_ENABLED = 1; // 0x1
+ field public static final int MIGRATION_OPTION_UNSPECIFIED = 0; // 0x0
+ }
+
+ public static final class ConnectionMigrationOptions.Builder {
+ ctor public ConnectionMigrationOptions.Builder();
+ method @NonNull public android.net.http.ConnectionMigrationOptions build();
+ method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setAllowNonDefaultNetworkUsage(int);
+ method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setDefaultNetworkMigration(int);
+ method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setPathDegradationMigration(int);
+ }
+
+ public final class DnsOptions {
+ method public int getPersistHostCache();
+ method @Nullable public java.time.Duration getPersistHostCachePeriod();
+ method public int getPreestablishConnectionsToStaleDnsResults();
+ method public int getStaleDns();
+ method @Nullable public android.net.http.DnsOptions.StaleDnsOptions getStaleDnsOptions();
+ method public int getUseHttpStackDnsResolver();
+ field public static final int DNS_OPTION_DISABLED = 2; // 0x2
+ field public static final int DNS_OPTION_ENABLED = 1; // 0x1
+ field public static final int DNS_OPTION_UNSPECIFIED = 0; // 0x0
+ }
+
+ public static final class DnsOptions.Builder {
+ ctor public DnsOptions.Builder();
+ method @NonNull public android.net.http.DnsOptions build();
+ method @NonNull public android.net.http.DnsOptions.Builder setPersistHostCache(int);
+ method @NonNull public android.net.http.DnsOptions.Builder setPersistHostCachePeriod(@NonNull java.time.Duration);
+ method @NonNull public android.net.http.DnsOptions.Builder setPreestablishConnectionsToStaleDnsResults(int);
+ method @NonNull public android.net.http.DnsOptions.Builder setStaleDns(int);
+ method @NonNull public android.net.http.DnsOptions.Builder setStaleDnsOptions(@NonNull android.net.http.DnsOptions.StaleDnsOptions);
+ method @NonNull public android.net.http.DnsOptions.Builder setUseHttpStackDnsResolver(int);
+ }
+
+ public static class DnsOptions.StaleDnsOptions {
+ method public int getAllowCrossNetworkUsage();
+ method @Nullable public java.time.Duration getFreshLookupTimeout();
+ method @Nullable public java.time.Duration getMaxExpiredDelay();
+ method public int getUseStaleOnNameNotResolved();
+ }
+
+ public static final class DnsOptions.StaleDnsOptions.Builder {
+ ctor public DnsOptions.StaleDnsOptions.Builder();
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions build();
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setAllowCrossNetworkUsage(int);
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setFreshLookupTimeout(@NonNull java.time.Duration);
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setMaxExpiredDelay(@NonNull java.time.Duration);
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setUseStaleOnNameNotResolved(int);
+ }
+
+ public abstract class HeaderBlock {
+ ctor public HeaderBlock();
+ method @NonNull public abstract java.util.List<java.util.Map.Entry<java.lang.String,java.lang.String>> getAsList();
+ method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> getAsMap();
+ }
+
+ public abstract class HttpEngine {
+ method public void bindToNetwork(@Nullable android.net.Network);
+ method @NonNull public abstract java.net.URLStreamHandlerFactory createUrlStreamHandlerFactory();
+ method @NonNull public static String getVersionString();
+ method @NonNull public abstract android.net.http.BidirectionalStream.Builder newBidirectionalStreamBuilder(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.http.BidirectionalStream.Callback);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder newUrlRequestBuilder(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.http.UrlRequest.Callback);
+ method @NonNull public abstract java.net.URLConnection openConnection(@NonNull java.net.URL) throws java.io.IOException;
+ method public abstract void shutdown();
+ }
+
+ public static class HttpEngine.Builder {
+ ctor public HttpEngine.Builder(@NonNull android.content.Context);
+ method @NonNull public android.net.http.HttpEngine.Builder addPublicKeyPins(@NonNull String, @NonNull java.util.Set<byte[]>, boolean, @NonNull java.time.Instant);
+ method @NonNull public android.net.http.HttpEngine.Builder addQuicHint(@NonNull String, int, int);
+ method @NonNull public android.net.http.HttpEngine build();
+ method @NonNull public String getDefaultUserAgent();
+ method @NonNull public android.net.http.HttpEngine.Builder setConnectionMigrationOptions(@NonNull android.net.http.ConnectionMigrationOptions);
+ method @NonNull public android.net.http.HttpEngine.Builder setDnsOptions(@NonNull android.net.http.DnsOptions);
+ method @NonNull public android.net.http.HttpEngine.Builder setEnableBrotli(boolean);
+ method @NonNull public android.net.http.HttpEngine.Builder setEnableHttp2(boolean);
+ method @NonNull public android.net.http.HttpEngine.Builder setEnableHttpCache(int, long);
+ method @NonNull public android.net.http.HttpEngine.Builder setEnablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
+ method @NonNull public android.net.http.HttpEngine.Builder setEnableQuic(boolean);
+ method @NonNull public android.net.http.HttpEngine.Builder setQuicOptions(@NonNull android.net.http.QuicOptions);
+ method @NonNull public android.net.http.HttpEngine.Builder setStoragePath(@NonNull String);
+ method @NonNull public android.net.http.HttpEngine.Builder setUserAgent(@NonNull String);
+ field public static final int HTTP_CACHE_DISABLED = 0; // 0x0
+ field public static final int HTTP_CACHE_DISK = 3; // 0x3
+ field public static final int HTTP_CACHE_DISK_NO_HTTP = 2; // 0x2
+ field public static final int HTTP_CACHE_IN_MEMORY = 1; // 0x1
+ }
+
+ public class HttpException extends java.io.IOException {
+ ctor public HttpException(@Nullable String, @Nullable Throwable);
+ }
+
+ public final class InlineExecutionProhibitedException extends java.util.concurrent.RejectedExecutionException {
+ ctor public InlineExecutionProhibitedException();
+ }
+
+ public abstract class NetworkException extends android.net.http.HttpException {
+ ctor public NetworkException(@Nullable String, @Nullable Throwable);
+ method public abstract int getErrorCode();
+ method public abstract boolean isImmediatelyRetryable();
+ field public static final int ERROR_ADDRESS_UNREACHABLE = 9; // 0x9
+ field public static final int ERROR_CONNECTION_CLOSED = 5; // 0x5
+ field public static final int ERROR_CONNECTION_REFUSED = 7; // 0x7
+ field public static final int ERROR_CONNECTION_RESET = 8; // 0x8
+ field public static final int ERROR_CONNECTION_TIMED_OUT = 6; // 0x6
+ field public static final int ERROR_HOSTNAME_NOT_RESOLVED = 1; // 0x1
+ field public static final int ERROR_INTERNET_DISCONNECTED = 2; // 0x2
+ field public static final int ERROR_NETWORK_CHANGED = 3; // 0x3
+ field public static final int ERROR_OTHER = 11; // 0xb
+ field public static final int ERROR_QUIC_PROTOCOL_FAILED = 10; // 0xa
+ field public static final int ERROR_TIMED_OUT = 4; // 0x4
+ }
+
+ public abstract class QuicException extends android.net.http.NetworkException {
+ ctor protected QuicException(@Nullable String, @Nullable Throwable);
+ }
+
+ public class QuicOptions {
+ method @NonNull public java.util.Set<java.lang.String> getAllowedQuicHosts();
+ method @Nullable public String getHandshakeUserAgent();
+ method @Nullable public java.time.Duration getIdleConnectionTimeout();
+ method public int getInMemoryServerConfigsCacheSize();
+ method public boolean hasInMemoryServerConfigsCacheSize();
+ }
+
+ public static final class QuicOptions.Builder {
+ ctor public QuicOptions.Builder();
+ method @NonNull public android.net.http.QuicOptions.Builder addAllowedQuicHost(@NonNull String);
+ method @NonNull public android.net.http.QuicOptions build();
+ method @NonNull public android.net.http.QuicOptions.Builder setHandshakeUserAgent(@NonNull String);
+ method @NonNull public android.net.http.QuicOptions.Builder setIdleConnectionTimeout(@NonNull java.time.Duration);
+ method @NonNull public android.net.http.QuicOptions.Builder setInMemoryServerConfigsCacheSize(int);
+ }
+
+ public abstract class UploadDataProvider implements java.io.Closeable {
+ ctor public UploadDataProvider();
+ method public void close() throws java.io.IOException;
+ method public abstract long getLength() throws java.io.IOException;
+ method public abstract void read(@NonNull android.net.http.UploadDataSink, @NonNull java.nio.ByteBuffer) throws java.io.IOException;
+ method public abstract void rewind(@NonNull android.net.http.UploadDataSink) throws java.io.IOException;
+ }
+
+ public abstract class UploadDataSink {
+ ctor public UploadDataSink();
+ method public abstract void onReadError(@NonNull Exception);
+ method public abstract void onReadSucceeded(boolean);
+ method public abstract void onRewindError(@NonNull Exception);
+ method public abstract void onRewindSucceeded();
+ }
+
+ public abstract class UrlRequest {
+ method public abstract void cancel();
+ method public abstract void followRedirect();
+ method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
+ method @Nullable public abstract String getHttpMethod();
+ method public abstract int getPriority();
+ method public abstract void getStatus(@NonNull android.net.http.UrlRequest.StatusListener);
+ method public abstract int getTrafficStatsTag();
+ method public abstract int getTrafficStatsUid();
+ method public abstract boolean hasTrafficStatsTag();
+ method public abstract boolean hasTrafficStatsUid();
+ method public abstract boolean isCacheDisabled();
+ method public abstract boolean isDirectExecutorAllowed();
+ method public abstract boolean isDone();
+ method public abstract void read(@NonNull java.nio.ByteBuffer);
+ method public abstract void start();
+ field public static final int REQUEST_PRIORITY_HIGHEST = 4; // 0x4
+ field public static final int REQUEST_PRIORITY_IDLE = 0; // 0x0
+ field public static final int REQUEST_PRIORITY_LOW = 2; // 0x2
+ field public static final int REQUEST_PRIORITY_LOWEST = 1; // 0x1
+ field public static final int REQUEST_PRIORITY_MEDIUM = 3; // 0x3
+ }
+
+ public abstract static class UrlRequest.Builder {
+ method @NonNull public abstract android.net.http.UrlRequest.Builder addHeader(@NonNull String, @NonNull String);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder bindToNetwork(@Nullable android.net.Network);
+ method @NonNull public abstract android.net.http.UrlRequest build();
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setCacheDisabled(boolean);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setDirectExecutorAllowed(boolean);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setHttpMethod(@NonNull String);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setPriority(int);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setTrafficStatsTag(int);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setTrafficStatsUid(int);
+ method @NonNull public abstract android.net.http.UrlRequest.Builder setUploadDataProvider(@NonNull android.net.http.UploadDataProvider, @NonNull java.util.concurrent.Executor);
+ }
+
+ public static interface UrlRequest.Callback {
+ method public void onCanceled(@NonNull android.net.http.UrlRequest, @Nullable android.net.http.UrlResponseInfo);
+ method public void onFailed(@NonNull android.net.http.UrlRequest, @Nullable android.net.http.UrlResponseInfo, @NonNull android.net.http.HttpException);
+ method public void onReadCompleted(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo, @NonNull java.nio.ByteBuffer) throws java.lang.Exception;
+ method public void onRedirectReceived(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo, @NonNull String) throws java.lang.Exception;
+ method public void onResponseStarted(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo) throws java.lang.Exception;
+ method public void onSucceeded(@NonNull android.net.http.UrlRequest, @NonNull android.net.http.UrlResponseInfo);
+ }
+
+ public static class UrlRequest.Status {
+ field public static final int CONNECTING = 10; // 0xa
+ field public static final int DOWNLOADING_PAC_FILE = 5; // 0x5
+ field public static final int ESTABLISHING_PROXY_TUNNEL = 8; // 0x8
+ field public static final int IDLE = 0; // 0x0
+ field public static final int INVALID = -1; // 0xffffffff
+ field public static final int READING_RESPONSE = 14; // 0xe
+ field public static final int RESOLVING_HOST = 9; // 0x9
+ field public static final int RESOLVING_HOST_IN_PAC_FILE = 7; // 0x7
+ field public static final int RESOLVING_PROXY_FOR_URL = 6; // 0x6
+ field public static final int SENDING_REQUEST = 12; // 0xc
+ field public static final int SSL_HANDSHAKE = 11; // 0xb
+ field public static final int WAITING_FOR_AVAILABLE_SOCKET = 2; // 0x2
+ field public static final int WAITING_FOR_CACHE = 4; // 0x4
+ field public static final int WAITING_FOR_DELEGATE = 3; // 0x3
+ field public static final int WAITING_FOR_RESPONSE = 13; // 0xd
+ field public static final int WAITING_FOR_STALLED_SOCKET_POOL = 1; // 0x1
+ }
+
+ public static interface UrlRequest.StatusListener {
+ method public void onStatus(int);
+ }
+
+ public abstract class UrlResponseInfo {
+ ctor public UrlResponseInfo();
+ method @NonNull public abstract android.net.http.HeaderBlock getHeaders();
+ method public abstract int getHttpStatusCode();
+ method @NonNull public abstract String getHttpStatusText();
+ method @NonNull public abstract String getNegotiatedProtocol();
+ method public abstract long getReceivedByteCount();
+ method @NonNull public abstract String getUrl();
+ method @NonNull public abstract java.util.List<java.lang.String> getUrlChain();
+ method public abstract boolean wasCached();
+ }
+
+}
+
diff --git a/framework/cronet_disabled/api/current.txt b/framework/cronet_disabled/api/current.txt
new file mode 100644
index 0000000..672e3e2
--- /dev/null
+++ b/framework/cronet_disabled/api/current.txt
@@ -0,0 +1,527 @@
+// Signature format: 2.0
+package android.net {
+
+ public class CaptivePortal implements android.os.Parcelable {
+ method public int describeContents();
+ method public void ignoreNetwork();
+ method public void reportCaptivePortalDismissed();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
+ }
+
+ public class ConnectivityDiagnosticsManager {
+ method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+ method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+ }
+
+ public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+ ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
+ method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+ method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
+ method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
+ }
+
+ public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
+ ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+ method @NonNull public android.net.LinkProperties getLinkProperties();
+ method @NonNull public android.net.Network getNetwork();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public long getReportTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+ field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
+ field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+ field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+ field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+ field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+ field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+ field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+ field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+ field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+ field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+ field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+ field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
+ }
+
+ public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
+ ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+ method public int describeContents();
+ method public int getDetectionMethod();
+ method @NonNull public android.net.LinkProperties getLinkProperties();
+ method @NonNull public android.net.Network getNetwork();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public long getReportTimestamp();
+ method @NonNull public android.os.PersistableBundle getStallDetails();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+ field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+ field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+ field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+ field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+ field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+ }
+
+ public class ConnectivityManager {
+ method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+ method public boolean bindProcessToNetwork(@Nullable android.net.Network);
+ method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
+ method @Deprecated public boolean getBackgroundDataSetting();
+ method @Nullable public android.net.Network getBoundNetworkForProcess();
+ method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
+ method @Nullable public android.net.ProxyInfo getDefaultProxy();
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network);
+ method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference();
+ method @Nullable public byte[] getNetworkWatchlistConfigHash();
+ method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork();
+ method public int getRestrictBackgroundStatus();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
+ method public boolean isDefaultNetworkActive();
+ method @Deprecated public static boolean isNetworkTypeValid(int);
+ method public void registerBestMatchingNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+ method public void releaseNetworkRequest(@NonNull android.app.PendingIntent);
+ method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener);
+ method @Deprecated public void reportBadNetwork(@Nullable android.net.Network);
+ method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean);
+ method public boolean requestBandwidthUpdate(@NonNull android.net.Network);
+ method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int);
+ method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int);
+ method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+ method @Deprecated public void setNetworkPreference(int);
+ method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network);
+ method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+ method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent);
+ field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+ field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+ field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
+ field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+ field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+ field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+ field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+ field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo";
+ field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover";
+ field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
+ field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
+ field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
+ field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
+ field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
+ field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+ field public static final String EXTRA_REASON = "reason";
+ field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
+ field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
+ field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2
+ field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+ field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+ field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+ field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7
+ field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8
+ field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9
+ field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0
+ field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4
+ field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
+ field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2
+ field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+ field @Deprecated public static final int TYPE_VPN = 17; // 0x11
+ field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
+ field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6
+ }
+
+ public static class ConnectivityManager.NetworkCallback {
+ ctor public ConnectivityManager.NetworkCallback();
+ ctor public ConnectivityManager.NetworkCallback(int);
+ method public void onAvailable(@NonNull android.net.Network);
+ method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
+ method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
+ method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties);
+ method public void onLosing(@NonNull android.net.Network, int);
+ method public void onLost(@NonNull android.net.Network);
+ method public void onUnavailable();
+ field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
+ }
+
+ public static interface ConnectivityManager.OnNetworkActiveListener {
+ method public void onNetworkActive();
+ }
+
+ public class DhcpInfo implements android.os.Parcelable {
+ ctor public DhcpInfo();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
+ field public int dns1;
+ field public int dns2;
+ field public int gateway;
+ field public int ipAddress;
+ field public int leaseDuration;
+ field public int netmask;
+ field public int serverAddress;
+ }
+
+ public final class DnsResolver {
+ method @NonNull public static android.net.DnsResolver getInstance();
+ method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+ method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+ method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+ method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+ field public static final int CLASS_IN = 1; // 0x1
+ field public static final int ERROR_PARSE = 0; // 0x0
+ field public static final int ERROR_SYSTEM = 1; // 0x1
+ field public static final int FLAG_EMPTY = 0; // 0x0
+ field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
+ field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
+ field public static final int FLAG_NO_RETRY = 1; // 0x1
+ field public static final int TYPE_A = 1; // 0x1
+ field public static final int TYPE_AAAA = 28; // 0x1c
+ }
+
+ public static interface DnsResolver.Callback<T> {
+ method public void onAnswer(@NonNull T, int);
+ method public void onError(@NonNull android.net.DnsResolver.DnsException);
+ }
+
+ public static class DnsResolver.DnsException extends java.lang.Exception {
+ ctor public DnsResolver.DnsException(int, @Nullable Throwable);
+ field public final int code;
+ }
+
+ public class InetAddresses {
+ method public static boolean isNumericAddress(@NonNull String);
+ method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
+ }
+
+ public final class IpConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.ProxyInfo getHttpProxy();
+ method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
+ }
+
+ public static final class IpConfiguration.Builder {
+ ctor public IpConfiguration.Builder();
+ method @NonNull public android.net.IpConfiguration build();
+ method @NonNull public android.net.IpConfiguration.Builder setHttpProxy(@Nullable android.net.ProxyInfo);
+ method @NonNull public android.net.IpConfiguration.Builder setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+ }
+
+ public final class IpPrefix implements android.os.Parcelable {
+ ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+ method public boolean contains(@NonNull java.net.InetAddress);
+ method public int describeContents();
+ method @NonNull public java.net.InetAddress getAddress();
+ method @IntRange(from=0, to=128) public int getPrefixLength();
+ method @NonNull public byte[] getRawAddress();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
+ }
+
+ public class LinkAddress implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.net.InetAddress getAddress();
+ method public int getFlags();
+ method @IntRange(from=0, to=128) public int getPrefixLength();
+ method public int getScope();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
+ }
+
+ public final class LinkProperties implements android.os.Parcelable {
+ ctor public LinkProperties();
+ method public boolean addRoute(@NonNull android.net.RouteInfo);
+ method public void clear();
+ method public int describeContents();
+ method @Nullable public java.net.Inet4Address getDhcpServerAddress();
+ method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+ method @Nullable public String getDomains();
+ method @Nullable public android.net.ProxyInfo getHttpProxy();
+ method @Nullable public String getInterfaceName();
+ method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
+ method public int getMtu();
+ method @Nullable public android.net.IpPrefix getNat64Prefix();
+ method @Nullable public String getPrivateDnsServerName();
+ method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
+ method public boolean isPrivateDnsActive();
+ method public boolean isWakeOnLanSupported();
+ method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
+ method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+ method public void setDomains(@Nullable String);
+ method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+ method public void setInterfaceName(@Nullable String);
+ method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
+ method public void setMtu(int);
+ method public void setNat64Prefix(@Nullable android.net.IpPrefix);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
+ }
+
+ public final class MacAddress implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
+ method @NonNull public static android.net.MacAddress fromString(@NonNull String);
+ method public int getAddressType();
+ method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
+ method public boolean isLocallyAssigned();
+ method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
+ method @NonNull public byte[] toByteArray();
+ method @NonNull public String toOuiString();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.net.MacAddress BROADCAST_ADDRESS;
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
+ field public static final int TYPE_BROADCAST = 3; // 0x3
+ field public static final int TYPE_MULTICAST = 2; // 0x2
+ field public static final int TYPE_UNICAST = 1; // 0x1
+ }
+
+ public class Network implements android.os.Parcelable {
+ method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
+ method public void bindSocket(java.net.Socket) throws java.io.IOException;
+ method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
+ method public int describeContents();
+ method public static android.net.Network fromNetworkHandle(long);
+ method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException;
+ method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException;
+ method public long getNetworkHandle();
+ method public javax.net.SocketFactory getSocketFactory();
+ method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+ method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
+ }
+
+ public final class NetworkCapabilities implements android.os.Parcelable {
+ ctor public NetworkCapabilities();
+ ctor public NetworkCapabilities(android.net.NetworkCapabilities);
+ method public int describeContents();
+ method @NonNull public int[] getCapabilities();
+ method @NonNull public int[] getEnterpriseIds();
+ method public int getLinkDownstreamBandwidthKbps();
+ method public int getLinkUpstreamBandwidthKbps();
+ method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+ method public int getOwnerUid();
+ method public int getSignalStrength();
+ method @Nullable public android.net.TransportInfo getTransportInfo();
+ method public boolean hasCapability(int);
+ method public boolean hasEnterpriseId(int);
+ method public boolean hasTransport(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
+ field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
+ field public static final int NET_CAPABILITY_CBS = 5; // 0x5
+ field public static final int NET_CAPABILITY_DUN = 2; // 0x2
+ field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+ field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
+ field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
+ field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+ field public static final int NET_CAPABILITY_HEAD_UNIT = 32; // 0x20
+ field public static final int NET_CAPABILITY_IA = 7; // 0x7
+ field public static final int NET_CAPABILITY_IMS = 4; // 0x4
+ field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
+ field public static final int NET_CAPABILITY_MCX = 23; // 0x17
+ field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+ field public static final int NET_CAPABILITY_MMTEL = 33; // 0x21
+ field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
+ field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
+ field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+ field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+ field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
+ field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
+ field public static final int NET_CAPABILITY_PRIORITIZE_BANDWIDTH = 35; // 0x23
+ field public static final int NET_CAPABILITY_PRIORITIZE_LATENCY = 34; // 0x22
+ field public static final int NET_CAPABILITY_RCS = 8; // 0x8
+ field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
+ field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
+ field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
+ field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
+ field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
+ field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
+ field public static final int NET_ENTERPRISE_ID_1 = 1; // 0x1
+ field public static final int NET_ENTERPRISE_ID_2 = 2; // 0x2
+ field public static final int NET_ENTERPRISE_ID_3 = 3; // 0x3
+ field public static final int NET_ENTERPRISE_ID_4 = 4; // 0x4
+ field public static final int NET_ENTERPRISE_ID_5 = 5; // 0x5
+ field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
+ field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
+ field public static final int TRANSPORT_CELLULAR = 0; // 0x0
+ field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+ field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+ field public static final int TRANSPORT_THREAD = 9; // 0x9
+ field public static final int TRANSPORT_USB = 8; // 0x8
+ field public static final int TRANSPORT_VPN = 4; // 0x4
+ field public static final int TRANSPORT_WIFI = 1; // 0x1
+ field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
+ }
+
+ @Deprecated public class NetworkInfo implements android.os.Parcelable {
+ ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String);
+ method @Deprecated public int describeContents();
+ method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState();
+ method @Deprecated public String getExtraInfo();
+ method @Deprecated public String getReason();
+ method @Deprecated public android.net.NetworkInfo.State getState();
+ method @Deprecated public int getSubtype();
+ method @Deprecated public String getSubtypeName();
+ method @Deprecated public int getType();
+ method @Deprecated public String getTypeName();
+ method @Deprecated public boolean isAvailable();
+ method @Deprecated public boolean isConnected();
+ method @Deprecated public boolean isConnectedOrConnecting();
+ method @Deprecated public boolean isFailover();
+ method @Deprecated public boolean isRoaming();
+ method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
+ }
+
+ @Deprecated public enum NetworkInfo.DetailedState {
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
+ }
+
+ @Deprecated public enum NetworkInfo.State {
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED;
+ enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN;
+ }
+
+ public class NetworkRequest implements android.os.Parcelable {
+ method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
+ method public int describeContents();
+ method @NonNull public int[] getCapabilities();
+ method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+ method @NonNull public int[] getTransportTypes();
+ method public boolean hasCapability(int);
+ method public boolean hasTransport(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
+ }
+
+ public static class NetworkRequest.Builder {
+ ctor public NetworkRequest.Builder();
+ ctor public NetworkRequest.Builder(@NonNull android.net.NetworkRequest);
+ method public android.net.NetworkRequest.Builder addCapability(int);
+ method public android.net.NetworkRequest.Builder addTransportType(int);
+ method public android.net.NetworkRequest build();
+ method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
+ method public android.net.NetworkRequest.Builder removeCapability(int);
+ method public android.net.NetworkRequest.Builder removeTransportType(int);
+ method @NonNull public android.net.NetworkRequest.Builder setIncludeOtherUidNetworks(boolean);
+ method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ }
+
+ public class ParseException extends java.lang.RuntimeException {
+ ctor public ParseException(@NonNull String);
+ ctor public ParseException(@NonNull String, @NonNull Throwable);
+ field public String response;
+ }
+
+ public class ProxyInfo implements android.os.Parcelable {
+ ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
+ method public static android.net.ProxyInfo buildDirectProxy(String, int);
+ method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>);
+ method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
+ method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int);
+ method public int describeContents();
+ method public String[] getExclusionList();
+ method public String getHost();
+ method public android.net.Uri getPacFileUrl();
+ method public int getPort();
+ method public boolean isValid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR;
+ }
+
+ public final class RouteInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.IpPrefix getDestination();
+ method @Nullable public java.net.InetAddress getGateway();
+ method @Nullable public String getInterface();
+ method public int getType();
+ method public boolean hasGateway();
+ method public boolean isDefaultRoute();
+ method public boolean matches(java.net.InetAddress);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
+ field public static final int RTN_THROW = 9; // 0x9
+ field public static final int RTN_UNICAST = 1; // 0x1
+ field public static final int RTN_UNREACHABLE = 7; // 0x7
+ }
+
+ public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ method public final void close();
+ method public final void start(@IntRange(from=0xa, to=0xe10) int);
+ method public final void stop();
+ field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
+ field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0
+ field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
+ field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+ field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+ field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
+ field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+ field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
+ field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
+ field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2
+ }
+
+ public static class SocketKeepalive.Callback {
+ ctor public SocketKeepalive.Callback();
+ method public void onDataReceived();
+ method public void onError(int);
+ method public void onStarted();
+ method public void onStopped();
+ }
+
+ public final class StaticIpConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+ method @Nullable public String getDomains();
+ method @Nullable public java.net.InetAddress getGateway();
+ method @NonNull public android.net.LinkAddress getIpAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
+ }
+
+ public static final class StaticIpConfiguration.Builder {
+ ctor public StaticIpConfiguration.Builder();
+ method @NonNull public android.net.StaticIpConfiguration build();
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
+ method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@NonNull android.net.LinkAddress);
+ }
+
+ public interface TransportInfo {
+ }
+
+}
+
diff --git a/framework/cronet_disabled/api/lint-baseline.txt b/framework/cronet_disabled/api/lint-baseline.txt
new file mode 100644
index 0000000..2f4004a
--- /dev/null
+++ b/framework/cronet_disabled/api/lint-baseline.txt
@@ -0,0 +1,4 @@
+// Baseline format: 1.0
+VisiblySynchronized: android.net.NetworkInfo#toString():
+ Internal locks must not be exposed (synchronizing on this or class is still
+ externally observable): method android.net.NetworkInfo.toString()
diff --git a/framework/cronet_disabled/api/module-lib-current.txt b/framework/cronet_disabled/api/module-lib-current.txt
new file mode 100644
index 0000000..193bd92
--- /dev/null
+++ b/framework/cronet_disabled/api/module-lib-current.txt
@@ -0,0 +1,239 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class ConnectivityFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
+ public class ConnectivityManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void addUidToMeteredNetworkAllowList(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void addUidToMeteredNetworkDenyList(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset();
+ method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshots();
+ method @Nullable public android.net.ProxyInfo getGlobalProxy();
+ method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
+ method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.LinkProperties getRedactedLinkPropertiesForPackage(@NonNull android.net.LinkProperties, int, @NonNull String);
+ method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public android.net.NetworkCapabilities getRedactedNetworkCapabilitiesForPackage(@NonNull android.net.NetworkCapabilities, int, @NonNull String);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkAllowList(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void removeUidFromMeteredNetworkDenyList(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void replaceFirewallChain(int, @NonNull int[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+ method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setFirewallChainEnabled(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreferences(@NonNull android.os.UserHandle, @NonNull java.util.List<android.net.ProfileNetworkPreference>, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setUidFirewallRule(int, int, int);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setVpnDefaultForUids(@NonNull String, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
+ method public void systemReady();
+ field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
+ field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
+ field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
+ field public static final String ACTION_PROMPT_UNVALIDATED = "android.net.action.PROMPT_UNVALIDATED";
+ field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
+ field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
+ field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000
+ field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
+ field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
+ field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
+ field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
+ field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
+ field public static final int BLOCKED_REASON_LOW_POWER_STANDBY = 32; // 0x20
+ field public static final int BLOCKED_REASON_NONE = 0; // 0x0
+ field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
+ field public static final int FIREWALL_CHAIN_DOZABLE = 1; // 0x1
+ field public static final int FIREWALL_CHAIN_LOW_POWER_STANDBY = 5; // 0x5
+ field public static final int FIREWALL_CHAIN_OEM_DENY_1 = 7; // 0x7
+ field public static final int FIREWALL_CHAIN_OEM_DENY_2 = 8; // 0x8
+ field public static final int FIREWALL_CHAIN_OEM_DENY_3 = 9; // 0x9
+ field public static final int FIREWALL_CHAIN_POWERSAVE = 3; // 0x3
+ field public static final int FIREWALL_CHAIN_RESTRICTED = 4; // 0x4
+ field public static final int FIREWALL_CHAIN_STANDBY = 2; // 0x2
+ field public static final int FIREWALL_RULE_ALLOW = 1; // 0x1
+ field public static final int FIREWALL_RULE_DEFAULT = 0; // 0x0
+ field public static final int FIREWALL_RULE_DENY = 2; // 0x2
+ field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_BLOCKING = 3; // 0x3
+ field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2; // 0x2
+ }
+
+ public static class ConnectivityManager.NetworkCallback {
+ method public void onBlockedStatusChanged(@NonNull android.net.Network, int);
+ }
+
+ public class ConnectivitySettingsManager {
+ method public static void clearGlobalProxy(@NonNull android.content.Context);
+ method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
+ method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
+ method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method @NonNull public static android.util.Range<java.lang.Integer> getDnsResolverSampleRanges(@NonNull android.content.Context);
+ method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int);
+ method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
+ method public static long getIngressRateLimitInBytesPerSecond(@NonNull android.content.Context);
+ method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+ method @NonNull public static java.util.Set<java.lang.Integer> getMobileDataPreferredUids(@NonNull android.content.Context);
+ method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context);
+ method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context);
+ method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int);
+ method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
+ method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
+ method public static int getPrivateDnsMode(@NonNull android.content.Context);
+ method @NonNull public static java.util.Set<java.lang.Integer> getUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context);
+ method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+ method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
+ method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
+ method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range<java.lang.Integer>);
+ method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int);
+ method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
+ method public static void setIngressRateLimitInBytesPerSecond(@NonNull android.content.Context, @IntRange(from=-1L, to=4294967295L) long);
+ method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+ method public static void setMobileDataPreferredUids(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
+ method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int);
+ method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String);
+ method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int);
+ method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int);
+ method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
+ method public static void setPrivateDnsMode(@NonNull android.content.Context, int);
+ method public static void setUidsAllowedOnRestrictedNetworks(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>);
+ method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+ method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
+ field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+ field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+ field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2
+ field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0
+ field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1
+ field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
+ field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
+ field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
+ }
+
+ public final class DhcpOption implements android.os.Parcelable {
+ ctor public DhcpOption(byte, @Nullable byte[]);
+ method public int describeContents();
+ method public byte getType();
+ method @Nullable public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpOption> CREATOR;
+ }
+
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method @Nullable public String getSubscriberId();
+ method public boolean isBypassableVpn();
+ method public boolean isVpnValidationRequired();
+ }
+
+ public static final class NetworkAgentConfig.Builder {
+ method @NonNull public android.net.NetworkAgentConfig.Builder setBypassableVpn(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLocalRoutesExcludedForVpn(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setVpnRequiresValidation(boolean);
+ }
+
+ public final class NetworkCapabilities implements android.os.Parcelable {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public java.util.Set<java.lang.Integer> getAllowedUids();
+ method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
+ method public boolean hasForbiddenCapability(int);
+ field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
+ field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L
+ field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L
+ field public static final long REDACT_FOR_NETWORK_SETTINGS = 4L; // 0x4L
+ field public static final long REDACT_NONE = 0L; // 0x0L
+ field public static final int TRANSPORT_TEST = 7; // 0x7
+ }
+
+ public static final class NetworkCapabilities.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAllowedUids(@NonNull java.util.Set<java.lang.Integer>);
+ method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
+ }
+
+ public class NetworkRequest implements android.os.Parcelable {
+ method @NonNull public int[] getEnterpriseIds();
+ method @NonNull public int[] getForbiddenCapabilities();
+ method public boolean hasEnterpriseId(int);
+ method public boolean hasForbiddenCapability(int);
+ }
+
+ public static class NetworkRequest.Builder {
+ method @NonNull public android.net.NetworkRequest.Builder addForbiddenCapability(int);
+ method @NonNull public android.net.NetworkRequest.Builder removeForbiddenCapability(int);
+ method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
+ }
+
+ public final class ProfileNetworkPreference implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int[] getExcludedUids();
+ method @NonNull public int[] getIncludedUids();
+ method public int getPreference();
+ method public int getPreferenceEnterpriseId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ProfileNetworkPreference> CREATOR;
+ }
+
+ public static final class ProfileNetworkPreference.Builder {
+ ctor public ProfileNetworkPreference.Builder();
+ method @NonNull public android.net.ProfileNetworkPreference build();
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setExcludedUids(@NonNull int[]);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setIncludedUids(@NonNull int[]);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setPreference(int);
+ method @NonNull public android.net.ProfileNetworkPreference.Builder setPreferenceEnterpriseId(int);
+ }
+
+ public final class TestNetworkInterface implements android.os.Parcelable {
+ ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
+ method public int describeContents();
+ method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
+ method @NonNull public String getInterfaceName();
+ method @Nullable public android.net.MacAddress getMacAddress();
+ method public int getMtu();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
+ }
+
+ public class TestNetworkManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTapInterface();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void teardownTestNetwork(@NonNull android.net.Network);
+ field public static final String TEST_TAP_PREFIX = "testtap";
+ }
+
+ public final class TestNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ ctor public TestNetworkSpecifier(@NonNull String);
+ method public int describeContents();
+ method @Nullable public String getInterfaceName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkSpecifier> CREATOR;
+ }
+
+ public interface TransportInfo {
+ method public default long getApplicableRedactions();
+ method @NonNull public default android.net.TransportInfo makeCopy(long);
+ }
+
+ public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+ ctor @Deprecated public VpnTransportInfo(int, @Nullable String);
+ method @Nullable public String getSessionId();
+ method @NonNull public android.net.VpnTransportInfo makeCopy(long);
+ }
+
+}
+
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/module-lib-removed.txt b/framework/cronet_disabled/api/module-lib-removed.txt
similarity index 100%
rename from Tethering/common/TetheringLib/cronet_enabled/api/module-lib-removed.txt
rename to framework/cronet_disabled/api/module-lib-removed.txt
diff --git a/framework/cronet_disabled/api/removed.txt b/framework/cronet_disabled/api/removed.txt
new file mode 100644
index 0000000..303a1e6
--- /dev/null
+++ b/framework/cronet_disabled/api/removed.txt
@@ -0,0 +1,11 @@
+// Signature format: 2.0
+package android.net {
+
+ public class ConnectivityManager {
+ method @Deprecated public boolean requestRouteToHost(int, int);
+ method @Deprecated public int startUsingNetworkFeature(int, String);
+ method @Deprecated public int stopUsingNetworkFeature(int, String);
+ }
+
+}
+
diff --git a/framework/cronet_disabled/api/system-current.txt b/framework/cronet_disabled/api/system-current.txt
new file mode 100644
index 0000000..4a2ed8a
--- /dev/null
+++ b/framework/cronet_disabled/api/system-current.txt
@@ -0,0 +1,544 @@
+// Signature format: 2.0
+package android.net {
+
+ public class CaptivePortal implements android.os.Parcelable {
+ method @Deprecated public void logEvent(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
+ method public void useNetwork();
+ field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
+ field public static final int APP_RETURN_DISMISSED = 0; // 0x0
+ field public static final int APP_RETURN_UNWANTED = 1; // 0x1
+ field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
+ }
+
+ public final class CaptivePortalData implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getByteLimit();
+ method public long getExpiryTimeMillis();
+ method public long getRefreshTimeMillis();
+ method @Nullable public android.net.Uri getUserPortalUrl();
+ method public int getUserPortalUrlSource();
+ method @Nullable public CharSequence getVenueFriendlyName();
+ method @Nullable public android.net.Uri getVenueInfoUrl();
+ method public int getVenueInfoUrlSource();
+ method public boolean isCaptive();
+ method public boolean isSessionExtendable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
+ field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+ }
+
+ public static class CaptivePortalData.Builder {
+ ctor public CaptivePortalData.Builder();
+ ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+ method @NonNull public android.net.CaptivePortalData build();
+ method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable CharSequence);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
+ }
+
+ public class ConnectivityManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+ method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.QosCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+ method public void unregisterQosCallback(@NonNull android.net.QosCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+ field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+ field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+ field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TYPE_NONE = -1; // 0xffffffff
+ field @Deprecated public static final int TYPE_PROXY = 16; // 0x10
+ field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
+ }
+
+ @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
+ ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
+ method @Deprecated public void onTetheringFailed();
+ method @Deprecated public void onTetheringStarted();
+ }
+
+ @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
+ method @Deprecated public void onTetheringEntitlementResult(int);
+ }
+
+ @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
+ ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
+ method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ public final class DscpPolicy implements android.os.Parcelable {
+ method @Nullable public java.net.InetAddress getDestinationAddress();
+ method @Nullable public android.util.Range<java.lang.Integer> getDestinationPortRange();
+ method public int getDscpValue();
+ method public int getPolicyId();
+ method public int getProtocol();
+ method @Nullable public java.net.InetAddress getSourceAddress();
+ method public int getSourcePort();
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DscpPolicy> CREATOR;
+ field public static final int PROTOCOL_ANY = -1; // 0xffffffff
+ field public static final int SOURCE_PORT_ANY = -1; // 0xffffffff
+ }
+
+ public static final class DscpPolicy.Builder {
+ ctor public DscpPolicy.Builder(int, int);
+ method @NonNull public android.net.DscpPolicy build();
+ method @NonNull public android.net.DscpPolicy.Builder setDestinationAddress(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.DscpPolicy.Builder setDestinationPortRange(@NonNull android.util.Range<java.lang.Integer>);
+ method @NonNull public android.net.DscpPolicy.Builder setProtocol(int);
+ method @NonNull public android.net.DscpPolicy.Builder setSourceAddress(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.DscpPolicy.Builder setSourcePort(int);
+ }
+
+ public final class InvalidPacketException extends java.lang.Exception {
+ ctor public InvalidPacketException(int);
+ method public int getError();
+ field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+ field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+ field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+ }
+
+ public final class IpConfiguration implements android.os.Parcelable {
+ ctor public IpConfiguration();
+ ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
+ method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
+ method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
+ method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+ method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
+ method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
+ method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+ }
+
+ public enum IpConfiguration.IpAssignment {
+ enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP;
+ enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC;
+ enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED;
+ }
+
+ public enum IpConfiguration.ProxySettings {
+ enum_constant public static final android.net.IpConfiguration.ProxySettings NONE;
+ enum_constant public static final android.net.IpConfiguration.ProxySettings PAC;
+ enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC;
+ enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED;
+ }
+
+ public final class IpPrefix implements android.os.Parcelable {
+ ctor public IpPrefix(@NonNull String);
+ }
+
+ public class KeepalivePacketData {
+ ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
+ method @NonNull public java.net.InetAddress getDstAddress();
+ method public int getDstPort();
+ method @NonNull public byte[] getPacket();
+ method @NonNull public java.net.InetAddress getSrcAddress();
+ method public int getSrcPort();
+ }
+
+ public class LinkAddress implements android.os.Parcelable {
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+ ctor public LinkAddress(@NonNull String);
+ ctor public LinkAddress(@NonNull String, int, int);
+ method public long getDeprecationTime();
+ method public long getExpirationTime();
+ method public boolean isGlobalPreferred();
+ method public boolean isIpv4();
+ method public boolean isIpv6();
+ method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
+ field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+ }
+
+ public final class LinkProperties implements android.os.Parcelable {
+ ctor public LinkProperties(@Nullable android.net.LinkProperties);
+ ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
+ method public boolean addDnsServer(@NonNull java.net.InetAddress);
+ method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
+ method public boolean addPcscfServer(@NonNull java.net.InetAddress);
+ method @NonNull public java.util.List<java.net.InetAddress> getAddresses();
+ method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
+ method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
+ method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
+ method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+ method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
+ method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
+ method @Nullable public String getTcpBufferSizes();
+ method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+ method public boolean hasGlobalIpv6Address();
+ method public boolean hasIpv4Address();
+ method public boolean hasIpv4DefaultRoute();
+ method public boolean hasIpv4DnsServer();
+ method public boolean hasIpv6DefaultRoute();
+ method public boolean hasIpv6DnsServer();
+ method public boolean isIpv4Provisioned();
+ method public boolean isIpv6Provisioned();
+ method public boolean isProvisioned();
+ method public boolean isReachable(@NonNull java.net.InetAddress);
+ method public boolean removeDnsServer(@NonNull java.net.InetAddress);
+ method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
+ method public boolean removeRoute(@NonNull android.net.RouteInfo);
+ method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+ method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
+ method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
+ method public void setPrivateDnsServerName(@Nullable String);
+ method public void setTcpBufferSizes(@Nullable String);
+ method public void setUsePrivateDns(boolean);
+ method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+ }
+
+ public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+ ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
+ }
+
+ public class Network implements android.os.Parcelable {
+ ctor public Network(@NonNull android.net.Network);
+ method public int getNetId();
+ method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
+ }
+
+ public abstract class NetworkAgent {
+ ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+ ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+ method @Nullable public android.net.Network getNetwork();
+ method public void markConnected();
+ method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
+ method public void onAutomaticReconnectDisabled();
+ method public void onBandwidthUpdateRequested();
+ method public void onDscpPolicyStatusUpdated(int, int);
+ method public void onNetworkCreated();
+ method public void onNetworkDestroyed();
+ method public void onNetworkUnwanted();
+ method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
+ method public void onQosCallbackUnregistered(int);
+ method public void onRemoveKeepalivePacketFilter(int);
+ method public void onSaveAcceptUnvalidated(boolean);
+ method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
+ method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
+ method public void onStopSocketKeepalive(int);
+ method public void onValidationStatus(int, @Nullable android.net.Uri);
+ method @NonNull public android.net.Network register();
+ method public void sendAddDscpPolicy(@NonNull android.net.DscpPolicy);
+ method public void sendLinkProperties(@NonNull android.net.LinkProperties);
+ method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+ method public void sendNetworkScore(@NonNull android.net.NetworkScore);
+ method public void sendNetworkScore(@IntRange(from=0, to=99) int);
+ method public final void sendQosCallbackError(int, int);
+ method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
+ method public final void sendQosSessionLost(int, int, int);
+ method public void sendRemoveAllDscpPolicies();
+ method public void sendRemoveDscpPolicy(int);
+ method public final void sendSocketKeepaliveEvent(int, int);
+ method @Deprecated public void setLegacySubtype(int, @NonNull String);
+ method public void setLingerDuration(@NonNull java.time.Duration);
+ method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int);
+ method public void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
+ method public void unregister();
+ method public void unregisterAfterReplacement(@IntRange(from=0, to=0x1388) int);
+ field public static final int DSCP_POLICY_STATUS_DELETED = 4; // 0x4
+ field public static final int DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES = 3; // 0x3
+ field public static final int DSCP_POLICY_STATUS_POLICY_NOT_FOUND = 5; // 0x5
+ field public static final int DSCP_POLICY_STATUS_REQUESTED_CLASSIFIER_NOT_SUPPORTED = 2; // 0x2
+ field public static final int DSCP_POLICY_STATUS_REQUEST_DECLINED = 1; // 0x1
+ field public static final int DSCP_POLICY_STATUS_SUCCESS = 0; // 0x0
+ field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
+ field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+ }
+
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getLegacyType();
+ method @NonNull public String getLegacyTypeName();
+ method public boolean isExplicitlySelected();
+ method public boolean isPartialConnectivityAcceptable();
+ method public boolean isUnvalidatedConnectivityAcceptable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+ }
+
+ public static final class NetworkAgentConfig.Builder {
+ ctor public NetworkAgentConfig.Builder();
+ method @NonNull public android.net.NetworkAgentConfig build();
+ method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyExtraInfo(@NonNull String);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubType(int);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubTypeName(@NonNull String);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setNat64DetectionEnabled(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setProvisioningNotificationEnabled(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
+ }
+
+ public final class NetworkCapabilities implements android.os.Parcelable {
+ method @NonNull public int[] getAdministratorUids();
+ method @Nullable public static String getCapabilityCarrierName(int);
+ method @Nullable public String getSsid();
+ method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
+ method @NonNull public int[] getTransportTypes();
+ method @Nullable public java.util.List<android.net.Network> getUnderlyingNetworks();
+ method public boolean isPrivateDnsBroken();
+ method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_BIP = 31; // 0x1f
+ field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
+ field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+ field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
+ field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
+ field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
+ field public static final int NET_CAPABILITY_VSIM = 30; // 0x1e
+ }
+
+ public static final class NetworkCapabilities.Builder {
+ ctor public NetworkCapabilities.Builder();
+ ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
+ method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder addEnterpriseId(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
+ method @NonNull public android.net.NetworkCapabilities build();
+ method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder removeEnterpriseId(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+ method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
+ method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+ method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
+ method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
+ method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities();
+ }
+
+ public class NetworkProvider {
+ ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
+ method public int getProviderId();
+ method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
+ method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void registerNetworkOffer(@NonNull android.net.NetworkScore, @NonNull android.net.NetworkCapabilities, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkProvider.NetworkOfferCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkOffer(@NonNull android.net.NetworkProvider.NetworkOfferCallback);
+ field public static final int ID_NONE = -1; // 0xffffffff
+ }
+
+ public static interface NetworkProvider.NetworkOfferCallback {
+ method public void onNetworkNeeded(@NonNull android.net.NetworkRequest);
+ method public void onNetworkUnneeded(@NonNull android.net.NetworkRequest);
+ }
+
+ public class NetworkReleasedException extends java.lang.Exception {
+ ctor public NetworkReleasedException();
+ }
+
+ public class NetworkRequest implements android.os.Parcelable {
+ method @Nullable public String getRequestorPackageName();
+ method public int getRequestorUid();
+ }
+
+ public static class NetworkRequest.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
+ method @NonNull public android.net.NetworkRequest.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
+ public final class NetworkScore implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getKeepConnectedReason();
+ method public int getLegacyInt();
+ method public boolean isExiting();
+ method public boolean isTransportPrimary();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+ field public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; // 0x1
+ field public static final int KEEP_CONNECTED_NONE = 0; // 0x0
+ }
+
+ public static final class NetworkScore.Builder {
+ ctor public NetworkScore.Builder();
+ method @NonNull public android.net.NetworkScore build();
+ method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
+ method @NonNull public android.net.NetworkScore.Builder setKeepConnectedReason(int);
+ method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int);
+ method @NonNull public android.net.NetworkScore.Builder setTransportPrimary(boolean);
+ }
+
+ public final class OemNetworkPreferences implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getNetworkPreferences();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.OemNetworkPreferences> CREATOR;
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID = 1; // 0x1
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2; // 0x2
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY = 3; // 0x3
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; // 0x4
+ field public static final int OEM_NETWORK_PREFERENCE_UNINITIALIZED = 0; // 0x0
+ }
+
+ public static final class OemNetworkPreferences.Builder {
+ ctor public OemNetworkPreferences.Builder();
+ ctor public OemNetworkPreferences.Builder(@NonNull android.net.OemNetworkPreferences);
+ method @NonNull public android.net.OemNetworkPreferences.Builder addNetworkPreference(@NonNull String, int);
+ method @NonNull public android.net.OemNetworkPreferences build();
+ method @NonNull public android.net.OemNetworkPreferences.Builder clearNetworkPreference(@NonNull String);
+ }
+
+ public abstract class QosCallback {
+ ctor public QosCallback();
+ method public void onError(@NonNull android.net.QosCallbackException);
+ method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes);
+ method public void onQosSessionLost(@NonNull android.net.QosSession);
+ }
+
+ public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException {
+ }
+
+ public final class QosCallbackException extends java.lang.Exception {
+ ctor public QosCallbackException(@NonNull String);
+ ctor public QosCallbackException(@NonNull Throwable);
+ }
+
+ public abstract class QosFilter {
+ method @NonNull public abstract android.net.Network getNetwork();
+ method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
+ method public boolean matchesProtocol(int);
+ method public abstract boolean matchesRemoteAddress(@NonNull java.net.InetAddress, int, int);
+ }
+
+ public final class QosSession implements android.os.Parcelable {
+ ctor public QosSession(int, int);
+ method public int describeContents();
+ method public int getSessionId();
+ method public int getSessionType();
+ method public long getUniqueId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
+ field public static final int TYPE_EPS_BEARER = 1; // 0x1
+ field public static final int TYPE_NR_BEARER = 2; // 0x2
+ }
+
+ public interface QosSessionAttributes {
+ }
+
+ public final class QosSocketInfo implements android.os.Parcelable {
+ ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
+ ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.DatagramSocket) throws java.io.IOException;
+ method public int describeContents();
+ method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
+ method @NonNull public android.net.Network getNetwork();
+ method @Nullable public java.net.InetSocketAddress getRemoteSocketAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
+ }
+
+ public final class RouteInfo implements android.os.Parcelable {
+ ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
+ ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
+ method public int getMtu();
+ }
+
+ public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ method public final void start(@IntRange(from=0xa, to=0xe10) int, int, @Nullable android.net.Network);
+ field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf
+ field public static final int FLAG_AUTOMATIC_ON_OFF = 1; // 0x1
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
+ public class SocketLocalAddressChangedException extends java.lang.Exception {
+ ctor public SocketLocalAddressChangedException();
+ }
+
+ public class SocketNotBoundException extends java.lang.Exception {
+ ctor public SocketNotBoundException();
+ }
+
+ public class SocketNotConnectedException extends java.lang.Exception {
+ ctor public SocketNotConnectedException();
+ }
+
+ public class SocketRemoteAddressChangedException extends java.lang.Exception {
+ ctor public SocketRemoteAddressChangedException();
+ }
+
+ public final class StaticIpConfiguration implements android.os.Parcelable {
+ ctor public StaticIpConfiguration();
+ ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+ method public void addDnsServer(@NonNull java.net.InetAddress);
+ method public void clear();
+ method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
+ }
+
+ public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+ ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
+ method public int describeContents();
+ method public int getIpTos();
+ method public int getIpTtl();
+ method public int getTcpAck();
+ method public int getTcpSeq();
+ method public int getTcpWindow();
+ method public int getTcpWindowScale();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
+ }
+
+ public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+ ctor public VpnTransportInfo(int, @Nullable String, boolean, boolean);
+ method public boolean areLongLivedTcpConnectionsExpensive();
+ method public int describeContents();
+ method public int getType();
+ method public boolean isBypassable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+ }
+
+}
+
+package android.net.apf {
+
+ public final class ApfCapabilities implements android.os.Parcelable {
+ ctor public ApfCapabilities(int, int, int);
+ method public int describeContents();
+ method public static boolean getApfDrop8023Frames();
+ method @NonNull public static int[] getApfEtherTypeBlackList();
+ method public boolean hasDataAccess();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
+ field public final int apfPacketFormat;
+ field public final int apfVersionSupported;
+ field public final int maximumApfProgramSize;
+ }
+
+}
+
diff --git a/framework/cronet_disabled/api/system-lint-baseline.txt b/framework/cronet_disabled/api/system-lint-baseline.txt
new file mode 100644
index 0000000..9a97707
--- /dev/null
+++ b/framework/cronet_disabled/api/system-lint-baseline.txt
@@ -0,0 +1 @@
+// Baseline format: 1.0
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/system-removed.txt b/framework/cronet_disabled/api/system-removed.txt
similarity index 100%
rename from Tethering/common/TetheringLib/cronet_enabled/api/system-removed.txt
rename to framework/cronet_disabled/api/system-removed.txt
diff --git a/framework/jarjar-excludes.txt b/framework/jarjar-excludes.txt
index 1311765..9b48d57 100644
--- a/framework/jarjar-excludes.txt
+++ b/framework/jarjar-excludes.txt
@@ -23,3 +23,8 @@
# TODO (b/217115866): add jarjar rules for Nearby
android\.nearby\..+
+
+# Don't touch anything that's already under android.net.http (cronet)
+# This is required since android.net.http contains api classes and hidden classes.
+# TODO: Remove this after hidden classes are moved to different package
+android\.net\.http\..+
\ No newline at end of file
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index 38e0059..ca297e5 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -23,6 +23,7 @@
#include <netinet/in.h>
#include <string.h>
+#include <bpf/BpfClassic.h>
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <nativehelper/JNIPlatformHelp.h>
#include <utils/Log.h>
@@ -55,11 +56,10 @@
static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jclass clazz, jobject javaFd)
{
- struct sock_filter filter_code[] = {
- // Reject all.
- BPF_STMT(BPF_RET | BPF_K, 0)
+ static struct sock_filter filter_code[] = {
+ BPF_REJECT,
};
- struct sock_fprog filter = {
+ static const struct sock_fprog filter = {
sizeof(filter_code) / sizeof(filter_code[0]),
filter_code,
};
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 381a18a..2315521 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -2535,6 +2535,26 @@
}
/**
+ * Get the supported keepalive count for each transport configured in resource overlays.
+ *
+ * @return An array of supported keepalive count for each transport type.
+ * @hide
+ */
+ @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_SETTINGS,
+ // CTS 13 used QUERY_ALL_PACKAGES to get the resource value, which was implemented
+ // as below in KeepaliveUtils. Also allow that permission so that KeepaliveUtils can
+ // use this method and avoid breaking released CTS. Apps that have this permission
+ // can query the resource themselves anyway.
+ android.Manifest.permission.QUERY_ALL_PACKAGES })
+ public int[] getSupportedKeepalives() {
+ try {
+ return mService.getSupportedKeepalives();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 822e67d..67dacb8 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -28,6 +28,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager.MultipathPreference;
import android.os.Binder;
import android.os.Build;
@@ -36,6 +37,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Range;
import com.android.net.module.util.ConnectivitySettingsUtils;
@@ -55,6 +57,7 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public class ConnectivitySettingsManager {
+ private static final String TAG = ConnectivitySettingsManager.class.getSimpleName();
private ConnectivitySettingsManager() {}
@@ -696,10 +699,20 @@
/**
* Set global http proxy settings from given {@link ProxyInfo}.
*
+ * <p class="note">
+ * While a {@link ProxyInfo} for a PAC proxy can be specified, not all devices support
+ * PAC proxies. In particular, smaller devices like watches often do not have the capabilities
+ * necessary to interpret the PAC file. In such cases, calling this API with a PAC proxy
+ * results in undefined behavior, including possibly breaking networking for applications.
+ * You can test for this by checking for the presence of {@link PackageManager.FEATURE_WEBVIEW}.
+ * </p>
+ *
* @param context The {@link Context} to set the setting.
* @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
* {@link ProxyInfo#buildPacProxy(Uri)} or
* {@link ProxyInfo#buildDirectProxy(String, int, List)}
+ * @throws UnsupportedOperationException if |proxyInfo| codes for a PAC proxy but the system
+ * does not support PAC proxies.
*/
public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
final String host = proxyInfo.getHost();
@@ -707,6 +720,14 @@
final String exclusionList = proxyInfo.getExclusionListAsString();
final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
+
+ if (!TextUtils.isEmpty(pacFileUrl)) {
+ final PackageManager pm = context.getPackageManager();
+ if (null != pm && !pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ Log.wtf(TAG, "PAC proxy can't be installed on a device without FEATURE_WEBVIEW");
+ }
+ }
+
if (TextUtils.isEmpty(pacFileUrl)) {
Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 1372e9a..ebe8bca 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -195,6 +195,8 @@
void stopKeepalive(in ISocketKeepaliveCallback cb);
+ int[] getSupportedKeepalives();
+
String getCaptivePortalServerUrl();
byte[] getNetworkWatchlistConfigHash();
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index e0926e9..9fb9bc6 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -1456,6 +1456,10 @@
* @hide
*/
public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
+ // Per 3GPP TS 24.229, B.2.2.1 PDP context activation and P-CSCF discovery
+ // list order is important, so on U+ compare one by one
+ if (SdkLevel.isAtLeastU()) return target.getPcscfServers().equals(mPcscfs);
+ // but for safety old behaviour on pre-U:
Collection<InetAddress> targetPcscfs = target.getPcscfServers();
return (mPcscfs.size() == targetPcscfs.size()) ?
mPcscfs.containsAll(targetPcscfs) : false;
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 3cc9c65..92e9599 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1348,6 +1348,18 @@
}
/**
+ * Gets the transports as an int. Internal callers only.
+ *
+ * Prefer getTransportTypes/hasTransportType if not immediately collapsing back into a scalar.
+ *
+ * @return a long integer representing the transport types.
+ * @hide
+ */
+ public long getTransportTypesInternal() {
+ return mTransportTypes;
+ }
+
+ /**
* Sets all the transports set on this {@code NetworkCapability} instance.
* This overwrites any existing transports.
*
diff --git a/framework/src/android/net/NetworkInfo.java b/framework/src/android/net/NetworkInfo.java
index b7ec519..7aa9847 100644
--- a/framework/src/android/net/NetworkInfo.java
+++ b/framework/src/android/net/NetworkInfo.java
@@ -334,6 +334,24 @@
}
/**
+ * Indicates whether this network is suspended.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ * @hide
+ */
+ @Deprecated
+ public boolean isSuspended() {
+ synchronized (this) {
+ return mState == State.SUSPENDED;
+ }
+ }
+
+ /**
* Indicates whether network connectivity is possible. A network is unavailable
* when a persistent or semi-persistent condition prevents the possibility
* of connecting to that network. Examples include
diff --git a/framework/src/android/net/SocketKeepalive.java b/framework/src/android/net/SocketKeepalive.java
index 10daf17..f915e72 100644
--- a/framework/src/android/net/SocketKeepalive.java
+++ b/framework/src/android/net/SocketKeepalive.java
@@ -149,9 +149,7 @@
public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
/**
- * There was no such slot. This should only be internally as it indicates
- * a programming error in the system server. It should not propagate to
- * applications.
+ * There was no such slot, or no keepalive running on this slot.
* @hide
*/
@SystemApi
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index b64299f..416c6de 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -260,7 +260,7 @@
/**
* Create a tap interface with or without carrier for testing purposes.
*
- * Note: setting carrierUp = false is not supported until kernel version 5.0.
+ * Note: setting carrierUp = false is not supported until kernel version 6.0.
*
* @param carrierUp whether the created interface has a carrier or not.
* @param bringUp whether to bring up the interface before returning it.
@@ -280,6 +280,8 @@
/**
* Create a tap interface for testing purposes.
*
+ * Note: setting carrierUp = false is not supported until kernel version 6.0.
+ *
* @param carrierUp whether the created interface has a carrier or not.
* @param bringUp whether to bring up the interface before returning it.
* @param disableIpv6ProvisioningDelay whether to disable DAD and RS delay.
diff --git a/framework/src/android/net/apf/ApfCapabilities.java b/framework/src/android/net/apf/ApfCapabilities.java
index 64f14a1..fae2499 100644
--- a/framework/src/android/net/apf/ApfCapabilities.java
+++ b/framework/src/android/net/apf/ApfCapabilities.java
@@ -19,9 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityResources;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,8 +33,6 @@
*/
@SystemApi
public final class ApfCapabilities implements Parcelable {
- private static ConnectivityResources sResources;
-
/**
* Version of APF instruction set supported for packet filtering. 0 indicates no support for
* packet filtering using APF programs.
@@ -67,15 +62,6 @@
apfPacketFormat = in.readInt();
}
- @NonNull
- private static synchronized ConnectivityResources getResources(@NonNull Context ctx) {
- if (sResources == null) {
- sResources = new ConnectivityResources(ctx);
- }
- return sResources;
- }
-
-
@Override
public int describeContents() {
return 0;
diff --git a/framework/src/android/net/util/KeepaliveUtils.java b/framework/src/android/net/util/KeepaliveUtils.java
index 8d7a0b3..07b8c45 100644
--- a/framework/src/android/net/util/KeepaliveUtils.java
+++ b/framework/src/android/net/util/KeepaliveUtils.java
@@ -18,11 +18,8 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityResources;
+import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
-import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
/**
* Collection of utilities for socket keepalive offload.
@@ -33,64 +30,20 @@
public static final String TAG = "KeepaliveUtils";
- public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
- public KeepaliveDeviceConfigurationException(final String msg) {
- super(msg);
- }
- }
-
/**
* Read supported keepalive count for each transport type from overlay resource. This should be
* used to create a local variable store of resource customization, and use it as the input for
- * {@link getSupportedKeepalivesForNetworkCapabilities}.
+ * {@link #getSupportedKeepalivesForNetworkCapabilities}.
*
* @param context The context to read resource from.
* @return An array of supported keepalive count for each transport type.
+ * @deprecated This is used by CTS 13, but can be removed after switching it to
+ * {@link ConnectivityManager#getSupportedKeepalives()}.
*/
@NonNull
+ @Deprecated
public static int[] getSupportedKeepalives(@NonNull Context context) {
- String[] res = null;
- try {
- final ConnectivityResources connRes = new ConnectivityResources(context);
- // TODO: use R.id.config_networkSupportedKeepaliveCount directly
- final int id = connRes.get().getIdentifier("config_networkSupportedKeepaliveCount",
- "array", connRes.getResourcesContext().getPackageName());
- res = new ConnectivityResources(context).get().getStringArray(id);
- } catch (Resources.NotFoundException unused) {
- }
- if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
-
- final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1];
- for (final String row : res) {
- if (TextUtils.isEmpty(row)) {
- throw new KeepaliveDeviceConfigurationException("Empty string");
- }
- final String[] arr = row.split(",");
- if (arr.length != 2) {
- throw new KeepaliveDeviceConfigurationException("Invalid parameter length");
- }
-
- int transport;
- int supported;
- try {
- transport = Integer.parseInt(arr[0]);
- supported = Integer.parseInt(arr[1]);
- } catch (NumberFormatException e) {
- throw new KeepaliveDeviceConfigurationException("Invalid number format");
- }
-
- if (!NetworkCapabilities.isValidTransport(transport)) {
- throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
- }
-
- if (supported < 0) {
- throw new KeepaliveDeviceConfigurationException(
- "Invalid supported count " + supported + " for "
- + NetworkCapabilities.transportNameOf(transport));
- }
- ret[transport] = supported;
- }
- return ret;
+ return context.getSystemService(ConnectivityManager.class).getSupportedKeepalives();
}
/**
diff --git a/nearby/tests/multidevices/README.md b/nearby/tests/multidevices/README.md
index b64667c..9d086de 100644
--- a/nearby/tests/multidevices/README.md
+++ b/nearby/tests/multidevices/README.md
@@ -43,14 +43,24 @@
* Adjust Bluetooth profile configurations. \
The Fast Pair provider simulator is an opposite role to the seeker. It needs
to enable/disable the following Bluetooth profile:
- * Disable A2DP (profile_supported_a2dp)
- * Disable the AVRCP controller (profile_supported_avrcp_controller)
- * Enable A2DP sink (profile_supported_a2dp_sink)
- * Enable the HFP client connection service (profile_supported_hfpclient,
- hfp_client_connection_service_enabled)
- * Enable the AVRCP target (profile_supported_avrcp_target)
- * Enable the automatic audio focus request
- (a2dp_sink_automatically_request_audio_focus)
+ * Disable A2DP source (bluetooth.profile.a2dp.source.enabled)
+ * Enable A2DP sink (bluetooth.profile.a2dp.sink.enabled)
+ * Disable the AVRCP controller (bluetooth.profile.avrcp.controller.enabled)
+ * Enable the AVRCP target (bluetooth.profile.avrcp.target.enabled)
+ * Enable the HFP service (bluetooth.profile.hfp.ag.enabled, bluetooth.profile.hfp.hf.enabled)
+
+```makefile
+# The Bluetooth profiles that Fast Pair provider simulator expect to have enabled.
+PRODUCT_PRODUCT_PROPERTIES += \
+ bluetooth.device.default_name=FastPairProviderSimulator \
+ bluetooth.profile.a2dp.source.enabled=false \
+ bluetooth.profile.a2dp.sink.enabled=true \
+ bluetooth.profile.avrcp.controller.enabled=false \
+ bluetooth.profile.avrcp.target.enabled=true \
+ bluetooth.profile.hfp.ag.enabled=true \
+ bluetooth.profile.hfp.hf.enabled=true
+```
+
* Adjust Bluetooth TX power limitation in Bluetooth module and disable the
Fast Pair in Google Play service (aka GMS)
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
index e916c53..75fafb0 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/src/android/nearby/fastpair/provider/simulator/app/MainActivity.java
@@ -657,9 +657,7 @@
int desiredIoCapability = getIoCapabilityFromModelId(modelId);
- mBluetoothController.setIoCapability(
- /*ioCapabilityClassic=*/ desiredIoCapability,
- /*ioCapabilityBLE=*/ desiredIoCapability);
+ mBluetoothController.setIoCapability(desiredIoCapability);
runOnUiThread(() -> {
updateStringStatusView(
@@ -950,9 +948,7 @@
}
// Recover the IO capability.
- mBluetoothController.setIoCapability(
- /*ioCapabilityClassic=*/ IO_CAPABILITY_IO, /*ioCapabilityBLE=*/
- IO_CAPABILITY_KBDISP);
+ mBluetoothController.setIoCapability(IO_CAPABILITY_IO);
super.onDestroy();
}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
index 0cc0c92..345e8d2 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
@@ -50,23 +50,16 @@
}
/**
- * Sets the Input/Output capability of the device for both classic Bluetooth and BLE operations.
+ * Sets the Input/Output capability of the device for classic Bluetooth operations.
* Note: In order to let changes take effect, this method will make sure the Bluetooth stack is
* restarted by blocking calling thread.
*
* @param ioCapabilityClassic One of {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_NONE},
* ```
* {@link #IO_CAPABILITY_KBDISP} or more in {@link BluetoothAdapter}.
- * @param ioCapabilityBLE
- * ```
- * One of {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_NONE}, {@link
- * ```
- * #IO_CAPABILITY_KBDISP} or more in {@link BluetoothAdapter}.
- * ```
*/
- fun setIoCapability(ioCapabilityClassic: Int, ioCapabilityBLE: Int) {
+ fun setIoCapability(ioCapabilityClassic: Int) {
bluetoothAdapter.ioCapability = ioCapabilityClassic
- bluetoothAdapter.leIoCapability = ioCapabilityBLE
// Toggling airplane mode on/off to restart Bluetooth stack and reset the BLE.
try {
@@ -273,4 +266,4 @@
private const val TURN_AIRPLANE_MODE_OFF = 0
private const val TURN_AIRPLANE_MODE_ON = 1
}
-}
\ No newline at end of file
+}
diff --git a/netd/BpfBaseTest.cpp b/netd/BpfBaseTest.cpp
index 624d216..c979a7b 100644
--- a/netd/BpfBaseTest.cpp
+++ b/netd/BpfBaseTest.cpp
@@ -93,7 +93,7 @@
ASSERT_EQ(TEST_TAG, tagResult.value().tag);
ASSERT_EQ(0, close(sock));
// Check map periodically until sk destroy handler have done its job.
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 1000; i++) {
usleep(5000); // 5ms
tagResult = cookieTagMap.readValue(cookie);
if (!tagResult.ok()) {
@@ -101,7 +101,7 @@
return;
}
}
- FAIL() << "socket tag still exist after 50ms";
+ FAIL() << "socket tag still exist after 5s";
}
}
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 2b773c9..8081d12 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -32,7 +32,6 @@
namespace net {
using base::unique_fd;
-using bpf::NONEXISTENT_COOKIE;
using bpf::getSocketCookie;
using bpf::retrieveProgram;
using netdutils::Status;
@@ -185,7 +184,7 @@
}
uint64_t sock_cookie = getSocketCookie(sockFd);
- if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
+ if (!sock_cookie) return -errno;
UidTagValue newKey = {.uid = (uint32_t)chargeUid, .tag = tag};
@@ -249,7 +248,7 @@
int BpfHandler::untagSocket(int sockFd) {
uint64_t sock_cookie = getSocketCookie(sockFd);
- if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
+ if (!sock_cookie) return -errno;
if (!mCookieTagMap.isValid()) return -EPERM;
base::Result<void> res = mCookieTagMap.deleteValue(sock_cookie);
diff --git a/service-t/native/libs/libnetworkstats/Android.bp b/service-t/native/libs/libnetworkstats/Android.bp
index f40d388..0dfd0af 100644
--- a/service-t/native/libs/libnetworkstats/Android.bp
+++ b/service-t/native/libs/libnetworkstats/Android.bp
@@ -30,6 +30,7 @@
],
shared_libs: [
"libbase",
+ "libcutils",
"liblog",
],
static_libs: [
@@ -81,6 +82,7 @@
shared_libs: [
"libbase",
"liblog",
+ "libcutils",
"libandroid_net",
],
compile_multilib: "both",
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index 6aa0fb4..c5f9631 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -149,6 +149,18 @@
if (mIsTest) return; // Don't touch non-hermetic bpf in test.
if (mStarted) sPoller.Stop();
mStarted = false;
+
+ // Although this shouldn't be required, there seems to be some cases when we
+ // don't fill enough of a Perfetto Chunk for Perfetto to automatically commit
+ // the traced data. This manually flushes OnStop so we commit at least once.
+ NetworkTraceHandler::Trace([&](NetworkTraceHandler::TraceContext ctx) {
+ perfetto::LockedHandle<NetworkTraceHandler> handle =
+ ctx.GetDataSourceLocked();
+ // Trace is called for all active handlers, only flush our context. Since
+ // handle doesn't have a `.get()`, use `*` and `&` to get what it points to.
+ if (&(*handle) != this) return;
+ ctx.Flush();
+ });
}
void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
index 3de9897..d538368 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
@@ -15,10 +15,12 @@
*/
#define LOG_TAG "NetworkTrace"
+#define ATRACE_TAG ATRACE_TAG_NETWORK
#include "netdbpf/NetworkTracePoller.h"
#include <bpf/BpfUtils.h>
+#include <cutils/trace.h>
#include <log/log.h>
#include <perfetto/tracing/platform.h>
#include <perfetto/tracing/tracing.h>
@@ -133,6 +135,8 @@
return false;
}
+ ATRACE_INT("NetworkTracePackets", packets.size());
+
mCallback(packets);
return true;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index c5104d8..c47c572 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -19,10 +19,11 @@
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
+import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,6 +45,7 @@
import android.net.nsd.MDnsManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -53,13 +55,17 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.InetAddressUtils;
import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.ExecutorProvider;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
@@ -67,8 +73,8 @@
import com.android.server.connectivity.mdns.MdnsSearchOptions;
import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
import com.android.server.connectivity.mdns.MdnsServiceInfo;
-import com.android.server.connectivity.mdns.MdnsSocketClientBase;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -77,12 +83,8 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -104,8 +106,6 @@
*/
private static final String MDNS_DISCOVERY_MANAGER_VERSION = "mdns_discovery_manager_version";
private static final String LOCAL_DOMAIN_NAME = "local";
- // Max label length as per RFC 1034/1035
- private static final int MAX_LABEL_LENGTH = 63;
/**
* Enable advertising using the Java MdnsAdvertiser, instead of the legacy mdnsresponder
@@ -144,6 +144,7 @@
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
+ private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
private final Context mContext;
private final NsdStateMachine mNsdStateMachine;
@@ -159,6 +160,7 @@
private final MdnsSocketProvider mMdnsSocketProvider;
@NonNull
private final MdnsAdvertiser mAdvertiser;
+ private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
// WARNING : Accessing these values in any thread is not safe, it must only be changed in the
// state machine thread. If change this outside state machine, it will need to introduce
// synchronization.
@@ -179,6 +181,8 @@
private int mUniqueId = 1;
// The count of the connected legacy clients.
private int mLegacyClientCount = 0;
+ // The number of client that ever connected.
+ private int mClientNumberId = 1;
private static class MdnsListener implements MdnsServiceBrowserListener {
protected final int mClientId;
@@ -240,14 +244,14 @@
public void onServiceNameDiscovered(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_FOUND,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
@Override
public void onServiceNameRemoved(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_LOST,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
}
@@ -262,7 +266,7 @@
public void onServiceFound(MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
}
@@ -277,21 +281,21 @@
public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
@Override
public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
@Override
public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED_LOST,
- new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
+ new MdnsEvent(mClientId, serviceInfo));
}
}
@@ -301,14 +305,10 @@
private static class MdnsEvent {
final int mClientId;
@NonNull
- final String mRequestedServiceType;
- @NonNull
final MdnsServiceInfo mMdnsServiceInfo;
- MdnsEvent(int clientId, @NonNull String requestedServiceType,
- @NonNull MdnsServiceInfo mdnsServiceInfo) {
+ MdnsEvent(int clientId, @NonNull MdnsServiceInfo mdnsServiceInfo) {
mClientId = clientId;
- mRequestedServiceType = requestedServiceType;
mMdnsServiceInfo = mdnsServiceInfo;
}
}
@@ -332,6 +332,7 @@
mMDnsManager.startDaemon();
mIsDaemonStarted = true;
maybeScheduleStop();
+ mServiceLogs.log("Start mdns_responder daemon");
}
private void maybeStopDaemon() {
@@ -342,6 +343,7 @@
mMDnsManager.unregisterEventListener(mMDnsEventCallback);
mMDnsManager.stopDaemon();
mIsDaemonStarted = false;
+ mServiceLogs.log("Stop mdns_responder daemon");
}
private boolean isAnyRequestActive() {
@@ -401,7 +403,9 @@
final INsdManagerCallback cb = arg.callback;
try {
cb.asBinder().linkToDeath(arg.connector, 0);
- cInfo = new ClientInfo(cb, arg.useJavaBackend);
+ final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
+ cInfo = new ClientInfo(cb, arg.useJavaBackend,
+ mServiceLogs.forSubComponent(tag));
mClients.put(arg.connector, cInfo);
} catch (RemoteException e) {
Log.w(TAG, "Client " + clientId + " has already died");
@@ -559,18 +563,7 @@
*/
@NonNull
private String truncateServiceName(@NonNull String originalName) {
- // UTF-8 is at most 4 bytes per character; return early in the common case where
- // the name can't possibly be over the limit given its string length.
- if (originalName.length() <= MAX_LABEL_LENGTH / 4) return originalName;
-
- final Charset utf8 = StandardCharsets.UTF_8;
- final CharsetEncoder encoder = utf8.newEncoder();
- final ByteBuffer out = ByteBuffer.allocate(MAX_LABEL_LENGTH);
- // encode will write as many characters as possible to the out buffer, and just
- // return an overflow code if there were too many characters (no need to check the
- // return code here, this method truncates the name on purpose).
- encoder.encode(CharBuffer.wrap(originalName), out, true /* endOfInput */);
- return new String(out.array(), 0, out.position(), utf8);
+ return MdnsUtils.truncateServiceName(originalName, MAX_LABEL_LENGTH);
}
private void stopDiscoveryManagerRequest(ClientRequest request, int clientId, int id,
@@ -606,7 +599,10 @@
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
- final String serviceType = constructServiceType(info.getServiceType());
+ final Pair<String, String> typeAndSubtype =
+ parseTypeAndSubtype(info.getServiceType());
+ final String serviceType = typeAndSubtype == null
+ ? null : typeAndSubtype.first;
if (clientInfo.mUseJavaBackend
|| mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
@@ -620,14 +616,21 @@
maybeStartMonitoringSockets();
final MdnsListener listener =
new DiscoveryListener(clientId, id, info, listenServiceType);
- final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
- .setNetwork(info.getNetwork())
- .setIsPassiveMode(true)
- .build();
+ final MdnsSearchOptions.Builder optionsBuilder =
+ MdnsSearchOptions.newBuilder()
+ .setNetwork(info.getNetwork())
+ .setIsPassiveMode(true);
+ if (typeAndSubtype.second != null) {
+ // The parsing ensures subtype starts with an underscore.
+ // MdnsSearchOptions expects the underscore to not be present.
+ optionsBuilder.addSubtype(typeAndSubtype.second.substring(1));
+ }
mMdnsDiscoveryManager.registerListener(
- listenServiceType, listener, options);
+ listenServiceType, listener, optionsBuilder.build());
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
clientInfo.onDiscoverServicesStarted(clientId, info);
+ clientInfo.log("Register a DiscoveryListener " + id
+ + " for service type:" + listenServiceType);
} else {
maybeStartDaemon();
if (discoverServices(id, info)) {
@@ -669,6 +672,7 @@
if (request instanceof DiscoveryManagerRequest) {
stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
clientInfo.onStopDiscoverySucceeded(clientId);
+ clientInfo.log("Unregister the DiscoveryListener " + id);
} else {
removeRequestMap(clientId, id, clientInfo);
if (stopServiceDiscovery(id)) {
@@ -701,7 +705,9 @@
id = getUniqueId();
final NsdServiceInfo serviceInfo = args.serviceInfo;
final String serviceType = serviceInfo.getServiceType();
- final String registerServiceType = constructServiceType(serviceType);
+ final Pair<String, String> typeSubtype = parseTypeAndSubtype(serviceType);
+ final String registerServiceType = typeSubtype == null
+ ? null : typeSubtype.first;
if (clientInfo.mUseJavaBackend
|| mDeps.isMdnsAdvertiserEnabled(mContext)
|| useAdvertiserForType(registerServiceType)) {
@@ -716,7 +722,11 @@
serviceInfo.getServiceName()));
maybeStartMonitoringSockets();
- mAdvertiser.addService(id, serviceInfo);
+ // TODO: pass in the subtype as well. Including the subtype in the
+ // service type would generate service instance names like
+ // Name._subtype._sub._type._tcp, which is incorrect
+ // (it should be Name._type._tcp).
+ mAdvertiser.addService(id, serviceInfo, typeSubtype.second);
storeAdvertiserRequestMap(clientId, id, clientInfo);
} else {
maybeStartDaemon();
@@ -782,7 +792,10 @@
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
- final String serviceType = constructServiceType(info.getServiceType());
+ final Pair<String, String> typeSubtype =
+ parseTypeAndSubtype(info.getServiceType());
+ final String serviceType = typeSubtype == null
+ ? null : typeSubtype.first;
if (clientInfo.mUseJavaBackend
|| mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
@@ -804,6 +817,8 @@
mMdnsDiscoveryManager.registerListener(
resolveServiceType, listener, options);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
+ clientInfo.log("Register a ResolutionListener " + id
+ + " for service type:" + resolveServiceType);
} else {
if (clientInfo.mResolvedService != null) {
clientInfo.onResolveServiceFailed(
@@ -846,6 +861,7 @@
if (request instanceof DiscoveryManagerRequest) {
stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
clientInfo.onStopResolutionSucceeded(clientId);
+ clientInfo.log("Unregister the ResolutionListener " + id);
} else {
removeRequestMap(clientId, id, clientInfo);
if (stopResolveService(id)) {
@@ -872,7 +888,10 @@
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
- final String serviceType = constructServiceType(info.getServiceType());
+ final Pair<String, String> typeAndSubtype =
+ parseTypeAndSubtype(info.getServiceType());
+ final String serviceType = typeAndSubtype == null
+ ? null : typeAndSubtype.first;
if (serviceType == null) {
clientInfo.onServiceInfoCallbackRegistrationFailed(clientId,
NsdManager.FAILURE_BAD_PARAMETERS);
@@ -891,6 +910,8 @@
mMdnsDiscoveryManager.registerListener(
resolveServiceType, listener, options);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
+ clientInfo.log("Register a ServiceInfoListener " + id
+ + " for service type:" + resolveServiceType);
break;
}
case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
@@ -914,6 +935,7 @@
if (request instanceof DiscoveryManagerRequest) {
stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
clientInfo.onServiceInfoCallbackUnregistered(clientId);
+ clientInfo.log("Unregister the ServiceInfoListener " + id);
} else {
loge("Unregister failed with non-DiscoveryManagerRequest.");
}
@@ -1100,9 +1122,38 @@
return true;
}
- private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(final MdnsEvent event) {
+ @Nullable
+ private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent(
+ final MdnsEvent event, int code) {
final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
- final String serviceType = event.mRequestedServiceType;
+ final String[] typeArray = serviceInfo.getServiceType();
+ final String joinedType;
+ if (typeArray.length == 0
+ || !typeArray[typeArray.length - 1].equals(LOCAL_DOMAIN_NAME)) {
+ Log.wtf(TAG, "MdnsServiceInfo type does not end in .local: "
+ + Arrays.toString(typeArray));
+ return null;
+ } else {
+ joinedType = TextUtils.join(".",
+ Arrays.copyOfRange(typeArray, 0, typeArray.length - 1));
+ }
+ final String serviceType;
+ switch (code) {
+ case NsdManager.SERVICE_FOUND:
+ case NsdManager.SERVICE_LOST:
+ // For consistency with historical behavior, discovered service types have
+ // a dot at the end.
+ serviceType = joinedType + ".";
+ break;
+ case RESOLVE_SERVICE_SUCCEEDED:
+ // For consistency with historical behavior, resolved service types have
+ // a dot at the beginning.
+ serviceType = "." + joinedType;
+ break;
+ default:
+ serviceType = joinedType;
+ break;
+ }
final String serviceName = serviceInfo.getServiceInstanceName();
final NsdServiceInfo servInfo = new NsdServiceInfo(serviceName, serviceType);
final Network network = serviceInfo.getNetwork();
@@ -1127,7 +1178,9 @@
final MdnsEvent event = (MdnsEvent) obj;
final int clientId = event.mClientId;
- final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event);
+ final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
+ // Errors are already logged if null
+ if (info == null) return false;
if (DBG) {
Log.d(TAG, String.format("MdnsDiscoveryManager event code=%s transactionId=%d",
NsdManager.nameOf(code), transactionId));
@@ -1146,8 +1199,6 @@
break;
}
final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
- // Add '.' in front of the service type that aligns with historical behavior
- info.setServiceType("." + event.mRequestedServiceType);
info.setPort(serviceInfo.getPort());
Map<String, String> attrs = serviceInfo.getAttributes();
@@ -1221,16 +1272,10 @@
}
for (String ipv6Address : v6Addrs) {
try {
- final InetAddress addr = InetAddresses.parseNumericAddress(ipv6Address);
- if (addr.isLinkLocalAddress()) {
- final Inet6Address v6Addr = Inet6Address.getByAddress(
- null /* host */, addr.getAddress(),
- serviceInfo.getInterfaceIndex());
- addresses.add(v6Addr);
- } else {
- addresses.add(addr);
- }
- } catch (IllegalArgumentException | UnknownHostException e) {
+ final Inet6Address addr = (Inet6Address) InetAddresses.parseNumericAddress(
+ ipv6Address);
+ addresses.add(InetAddressUtils.withScopeId(addr, serviceInfo.getInterfaceIndex()));
+ } catch (IllegalArgumentException e) {
Log.wtf(TAG, "Invalid ipv6 address", e);
}
}
@@ -1290,28 +1335,39 @@
* Check the given service type is valid and construct it to a service type
* which can use for discovery / resolution service.
*
- * <p> The valid service type should be 2 labels, or 3 labels if the query is for a
+ * <p>The valid service type should be 2 labels, or 3 labels if the query is for a
* subtype (see RFC6763 7.1). Each label is up to 63 characters and must start with an
* underscore; they are alphanumerical characters or dashes or underscore, except the
* last one that is just alphanumerical. The last label must be _tcp or _udp.
*
+ * <p>The subtype may also be specified with a comma after the service type, for example
+ * _type._tcp,_subtype.
+ *
* @param serviceType the request service type for discovery / resolution service
* @return constructed service type or null if the given service type is invalid.
*/
@Nullable
- public static String constructServiceType(String serviceType) {
+ public static Pair<String, String> parseTypeAndSubtype(String serviceType) {
if (TextUtils.isEmpty(serviceType)) return null;
+ final String typeOrSubtypePattern = "_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]";
final Pattern serviceTypePattern = Pattern.compile(
- "^(_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]\\.)?"
- + "(_[a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]\\._(?:tcp|udp))"
+ // Optional leading subtype (_subtype._type._tcp)
+ // (?: xxx) is a non-capturing parenthesis, don't capture the dot
+ "^(?:(" + typeOrSubtypePattern + ")\\.)?"
+ // Actual type (_type._tcp.local)
+ + "(" + typeOrSubtypePattern + "\\._(?:tcp|udp))"
// Drop '.' at the end of service type that is compatible with old backend.
- + "\\.?$");
+ // e.g. allow "_type._tcp.local."
+ + "\\.?"
+ // Optional subtype after comma, for "_type._tcp,_subtype" format
+ + "(?:,(" + typeOrSubtypePattern + "))?"
+ + "$");
final Matcher matcher = serviceTypePattern.matcher(serviceType);
if (!matcher.matches()) return null;
- return matcher.group(1) == null
- ? matcher.group(2)
- : matcher.group(1) + "_sub." + matcher.group(2);
+ // Use the subtype either at the beginning or after the comma
+ final String subtype = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
+ return new Pair<>(matcher.group(2), subtype);
}
@VisibleForTesting
@@ -1329,17 +1385,18 @@
mMDnsEventCallback = new MDnsEventCallback(mNsdStateMachine);
mDeps = deps;
- mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
+ mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
+ LOGGER.forSubComponent("MdnsSocketProvider"));
// Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
// address events are received.
handler.post(mMdnsSocketProvider::startNetLinkMonitor);
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
- mMdnsDiscoveryManager =
- deps.makeMdnsDiscoveryManager(new ExecutorProvider(), mMdnsSocketClient);
+ mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
+ mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
- new AdvertiserCallback());
+ new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
}
/**
@@ -1354,8 +1411,8 @@
* @return true if the MdnsDiscoveryManager feature is enabled.
*/
public boolean isMdnsDiscoveryManagerEnabled(Context context) {
- return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context,
- NAMESPACE_CONNECTIVITY, MDNS_DISCOVERY_MANAGER_VERSION,
+ return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
+ MDNS_DISCOVERY_MANAGER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
false /* defaultEnabled */);
}
@@ -1366,8 +1423,9 @@
* @return true if the MdnsAdvertiser feature is enabled.
*/
public boolean isMdnsAdvertiserEnabled(Context context) {
- return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context,
- NAMESPACE_CONNECTIVITY, MDNS_ADVERTISER_VERSION, false /* defaultEnabled */);
+ return isAtLeastU() || DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING,
+ MDNS_ADVERTISER_VERSION, DeviceConfigUtils.TETHERING_MODULE_NAME,
+ false /* defaultEnabled */);
}
/**
@@ -1392,8 +1450,9 @@
* @see MdnsDiscoveryManager
*/
public MdnsDiscoveryManager makeMdnsDiscoveryManager(
- ExecutorProvider executorProvider, MdnsSocketClientBase socketClient) {
- return new MdnsDiscoveryManager(executorProvider, socketClient);
+ @NonNull ExecutorProvider executorProvider,
+ @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog) {
+ return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
}
/**
@@ -1401,15 +1460,16 @@
*/
public MdnsAdvertiser makeMdnsAdvertiser(
@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
- @NonNull MdnsAdvertiser.AdvertiserCallback cb) {
- return new MdnsAdvertiser(looper, socketProvider, cb);
+ @NonNull MdnsAdvertiser.AdvertiserCallback cb, @NonNull SharedLog sharedLog) {
+ return new MdnsAdvertiser(looper, socketProvider, cb, sharedLog);
}
/**
* @see MdnsSocketProvider
*/
- public MdnsSocketProvider makeMdnsSocketProvider(Context context, Looper looper) {
- return new MdnsSocketProvider(context, looper);
+ public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
+ @NonNull Looper looper, @NonNull SharedLog sharedLog) {
+ return new MdnsSocketProvider(context, looper, sharedLog);
}
}
@@ -1545,12 +1605,14 @@
@NonNull public final NsdServiceConnector connector;
@NonNull public final INsdManagerCallback callback;
public final boolean useJavaBackend;
+ public final int uid;
ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
- boolean useJavaBackend) {
+ boolean useJavaBackend, int uid) {
this.connector = connector;
this.callback = callback;
this.useJavaBackend = useJavaBackend;
+ this.uid = uid;
}
}
@@ -1559,9 +1621,9 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
if (DBG) Log.d(TAG, "New client connect. useJavaBackend=" + useJavaBackend);
final INsdServiceConnector connector = new NsdServiceConnector();
- mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
- NsdManager.REGISTER_CLIENT,
- new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend)));
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
+ new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend,
+ Binder.getCallingUid())));
return connector;
}
@@ -1760,15 +1822,19 @@
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!PermissionUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!PermissionUtils.checkDumpPermission(mContext, TAG, writer)) return;
- for (ClientInfo client : mClients.values()) {
- pw.println("Client Info");
- pw.println(client);
- }
-
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ // Dump state machine logs
mNsdStateMachine.dump(fd, pw, args);
+
+ // Dump service and clients logs
+ pw.println();
+ pw.println("Logs:");
+ pw.increaseIndent();
+ mServiceLogs.reverseDump(pw);
+ pw.decreaseIndent();
}
private abstract static class ClientRequest {
@@ -1819,11 +1885,14 @@
private boolean mIsPreSClient = false;
// The flag of using java backend if the client's target SDK >= U
private final boolean mUseJavaBackend;
+ // Store client logs
+ private final SharedLog mClientLogs;
- private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend) {
+ private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog) {
mCb = cb;
mUseJavaBackend = useJavaBackend;
- if (DBG) Log.d(TAG, "New client");
+ mClientLogs = sharedLog;
+ mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
}
@Override
@@ -1861,6 +1930,7 @@
// Remove any pending requests from the global map when we get rid of a client,
// and send cancellations to the daemon.
private void expungeAllRequests() {
+ mClientLogs.log("Client unregistered. expungeAllRequests!");
// TODO: to keep handler responsive, do not clean all requests for that client at once.
for (int i = 0; i < mClientRequests.size(); i++) {
final int clientId = mClientRequests.keyAt(i);
@@ -1915,6 +1985,10 @@
return -1;
}
+ private void log(String message) {
+ mClientLogs.log(message);
+ }
+
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
try {
mCb.onDiscoverServicesStarted(listenerKey, info);
diff --git a/service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/AbstractSocketNetlink.java
similarity index 95%
rename from service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java
rename to service-t/src/com/android/server/connectivity/mdns/AbstractSocketNetlink.java
index ef3928c..b792e46 100644
--- a/service-t/src/com/android/server/connectivity/mdns/ISocketNetLinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/AbstractSocketNetlink.java
@@ -19,7 +19,7 @@
/**
* The interface for netlink monitor.
*/
-public interface ISocketNetLinkMonitor {
+public interface AbstractSocketNetlink {
/**
* Returns if the netlink monitor is supported or not. By default, it is not supported.
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 9a67007..866ecba 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -211,7 +211,7 @@
| (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0));
}
- private void sendPacketTo(MdnsSocketClientBase requestSender, InetSocketAddress address)
+ private void sendPacketTo(MdnsSocketClient requestSender, InetSocketAddress address)
throws IOException {
DatagramPacket packet = packetWriter.getPacket(address);
if (expectUnicastResponse) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index ec3e997..5f27b6a 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkAddress;
@@ -28,6 +30,8 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.List;
import java.util.Map;
@@ -47,6 +51,7 @@
// Top-level domain for link-local queries, as per RFC6762 3.
private static final String LOCAL_TLD = "local";
+
private final Looper mLooper;
private final AdvertiserCallback mCb;
@@ -65,6 +70,7 @@
private final Dependencies mDeps;
private String[] mDeviceHostName;
+ @NonNull private final SharedLog mSharedLog;
/**
* Dependencies for {@link MdnsAdvertiser}, useful for testing.
@@ -78,11 +84,11 @@
@NonNull List<LinkAddress> initialAddresses,
@NonNull Looper looper, @NonNull byte[] packetCreationBuffer,
@NonNull MdnsInterfaceAdvertiser.Callback cb,
- @NonNull String[] deviceHostName) {
+ @NonNull String[] deviceHostName,
+ @NonNull SharedLog sharedLog) {
// Note NetworkInterface is final and not mockable
- final String logTag = socket.getInterface().getName();
- return new MdnsInterfaceAdvertiser(logTag, socket, initialAddresses, looper,
- packetCreationBuffer, cb, deviceHostName);
+ return new MdnsInterfaceAdvertiser(socket, initialAddresses, looper,
+ packetCreationBuffer, cb, deviceHostName, sharedLog);
}
/**
@@ -129,9 +135,7 @@
@Override
public void onServiceConflict(@NonNull MdnsInterfaceAdvertiser advertiser, int serviceId) {
- if (DBG) {
- Log.v(TAG, "Found conflict, restarted probing for service " + serviceId);
- }
+ mSharedLog.i("Found conflict, restarted probing for service " + serviceId);
final Registration registration = mRegistrations.get(serviceId);
if (registration == null) return;
@@ -249,8 +253,9 @@
int getConflictingService(@NonNull NsdServiceInfo info) {
for (int i = 0; i < mPendingRegistrations.size(); i++) {
final NsdServiceInfo other = mPendingRegistrations.valueAt(i).getServiceInfo();
- if (info.getServiceName().equals(other.getServiceName())
- && info.getServiceType().equals(other.getServiceType())) {
+ if (MdnsUtils.equalsIgnoreDnsCase(info.getServiceName(), other.getServiceName())
+ && MdnsUtils.equalsIgnoreDnsCase(info.getServiceType(),
+ other.getServiceType())) {
return mPendingRegistrations.keyAt(i);
}
}
@@ -266,7 +271,8 @@
mPendingRegistrations.put(id, registration);
for (int i = 0; i < mAdvertisers.size(); i++) {
try {
- mAdvertisers.valueAt(i).addService(id, registration.getServiceInfo());
+ mAdvertisers.valueAt(i).addService(
+ id, registration.getServiceInfo(), registration.getSubtype());
} catch (NameConflictException e) {
Log.wtf(TAG, "Name conflict adding services that should have unique names", e);
}
@@ -287,15 +293,17 @@
MdnsInterfaceAdvertiser advertiser = mAllAdvertisers.get(socket);
if (advertiser == null) {
advertiser = mDeps.makeAdvertiser(socket, addresses, mLooper, mPacketCreationBuffer,
- mInterfaceAdvertiserCb, mDeviceHostName);
+ mInterfaceAdvertiserCb, mDeviceHostName,
+ mSharedLog.forSubComponent(socket.getInterface().getName()));
mAllAdvertisers.put(socket, advertiser);
advertiser.start();
}
mAdvertisers.put(socket, advertiser);
for (int i = 0; i < mPendingRegistrations.size(); i++) {
+ final Registration registration = mPendingRegistrations.valueAt(i);
try {
advertiser.addService(mPendingRegistrations.keyAt(i),
- mPendingRegistrations.valueAt(i).getServiceInfo());
+ registration.getServiceInfo(), registration.getSubtype());
} catch (NameConflictException e) {
Log.wtf(TAG, "Name conflict adding services that should have unique names", e);
}
@@ -324,10 +332,13 @@
private int mConflictCount;
@NonNull
private NsdServiceInfo mServiceInfo;
+ @Nullable
+ private final String mSubtype;
- private Registration(@NonNull NsdServiceInfo serviceInfo) {
+ private Registration(@NonNull NsdServiceInfo serviceInfo, @Nullable String subtype) {
this.mOriginalName = serviceInfo.getServiceName();
this.mServiceInfo = serviceInfo;
+ this.mSubtype = subtype;
}
/**
@@ -358,7 +369,7 @@
// "Name (2)", then "Name (3)" etc.
// TODO: use a hidden method in NsdServiceInfo once MdnsAdvertiser is moved to service-t
final NsdServiceInfo newInfo = new NsdServiceInfo();
- newInfo.setServiceName(mOriginalName + " (" + (mConflictCount + renameCount + 1) + ")");
+ newInfo.setServiceName(getUpdatedServiceName(renameCount));
newInfo.setServiceType(mServiceInfo.getServiceType());
for (Map.Entry<String, byte[]> attr : mServiceInfo.getAttributes().entrySet()) {
newInfo.setAttribute(attr.getKey(),
@@ -371,10 +382,22 @@
return newInfo;
}
+ private String getUpdatedServiceName(int renameCount) {
+ final String suffix = " (" + (mConflictCount + renameCount + 1) + ")";
+ final String truncatedServiceName = MdnsUtils.truncateServiceName(mOriginalName,
+ MAX_LABEL_LENGTH - suffix.length());
+ return truncatedServiceName + suffix;
+ }
+
@NonNull
public NsdServiceInfo getServiceInfo() {
return mServiceInfo;
}
+
+ @Nullable
+ public String getSubtype() {
+ return mSubtype;
+ }
}
/**
@@ -405,18 +428,20 @@
}
public MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
- @NonNull AdvertiserCallback cb) {
- this(looper, socketProvider, cb, new Dependencies());
+ @NonNull AdvertiserCallback cb, @NonNull SharedLog sharedLog) {
+ this(looper, socketProvider, cb, new Dependencies(), sharedLog);
}
@VisibleForTesting
MdnsAdvertiser(@NonNull Looper looper, @NonNull MdnsSocketProvider socketProvider,
- @NonNull AdvertiserCallback cb, @NonNull Dependencies deps) {
+ @NonNull AdvertiserCallback cb, @NonNull Dependencies deps,
+ @NonNull SharedLog sharedLog) {
mLooper = looper;
mCb = cb;
mSocketProvider = socketProvider;
mDeps = deps;
mDeviceHostName = deps.generateHostname();
+ mSharedLog = sharedLog;
}
private void checkThread() {
@@ -429,8 +454,9 @@
* Add a service to advertise.
* @param id A unique ID for the service.
* @param service The service info to advertise.
+ * @param subtype An optional subtype to advertise the service with.
*/
- public void addService(int id, NsdServiceInfo service) {
+ public void addService(int id, NsdServiceInfo service, @Nullable String subtype) {
checkThread();
if (mRegistrations.get(id) != null) {
Log.e(TAG, "Adding duplicate registration for " + service);
@@ -439,12 +465,10 @@
return;
}
- if (DBG) {
- Log.i(TAG, "Adding service " + service + " with ID " + id);
- }
+ mSharedLog.i("Adding service " + service + " with ID " + id + " and subtype " + subtype);
final Network network = service.getNetwork();
- final Registration registration = new Registration(service);
+ final Registration registration = new Registration(service, subtype);
final BiPredicate<Network, InterfaceAdvertiserRequest> checkConflictFilter;
if (network == null) {
// If registering on all networks, no advertiser must have conflicts
@@ -473,9 +497,7 @@
public void removeService(int id) {
checkThread();
if (!mRegistrations.contains(id)) return;
- if (DBG) {
- Log.i(TAG, "Removing service with ID " + id);
- }
+ mSharedLog.i("Removing service with ID " + id);
for (int i = mAdvertiserRequests.size() - 1; i >= 0; i--) {
final InterfaceAdvertiserRequest advertiser = mAdvertiserRequests.valueAt(i);
advertiser.removeService(id);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index fb8af8d..d3cbf82 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -16,25 +16,27 @@
package com.android.server.connectivity.mdns;
-import static com.android.server.connectivity.mdns.MdnsSocketProvider.isNetworkMatched;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.net.Network;
-import android.text.TextUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* This class keeps tracking the set of registered {@link MdnsServiceBrowserListener} instances, and
@@ -43,13 +45,14 @@
public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
private static final String TAG = MdnsDiscoveryManager.class.getSimpleName();
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final MdnsLogger LOGGER = new MdnsLogger("MdnsDiscoveryManager");
private final ExecutorProvider executorProvider;
private final MdnsSocketClientBase socketClient;
+ @NonNull private final SharedLog sharedLog;
- @GuardedBy("this")
@NonNull private final PerNetworkServiceTypeClients perNetworkServiceTypeClients;
+ @NonNull private final Handler handler;
+ @Nullable private final HandlerThread handlerThread;
private static class PerNetworkServiceTypeClients {
private final ArrayMap<Pair<String, Network>, MdnsServiceTypeClient> clients =
@@ -57,32 +60,38 @@
public void put(@NonNull String serviceType, @Nullable Network network,
@NonNull MdnsServiceTypeClient client) {
- final Pair<String, Network> perNetworkServiceType = new Pair<>(serviceType, network);
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
+ final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType,
+ network);
clients.put(perNetworkServiceType, client);
}
@Nullable
public MdnsServiceTypeClient get(@NonNull String serviceType, @Nullable Network network) {
- final Pair<String, Network> perNetworkServiceType = new Pair<>(serviceType, network);
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
+ final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType,
+ network);
return clients.getOrDefault(perNetworkServiceType, null);
}
public List<MdnsServiceTypeClient> getByServiceType(@NonNull String serviceType) {
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) {
final Pair<String, Network> perNetworkServiceType = clients.keyAt(i);
- if (serviceType.equals(perNetworkServiceType.first)) {
+ if (dnsLowerServiceType.equals(perNetworkServiceType.first)) {
list.add(clients.valueAt(i));
}
}
return list;
}
- public List<MdnsServiceTypeClient> getByMatchingNetwork(@Nullable Network network) {
+ public List<MdnsServiceTypeClient> getByNetwork(@Nullable Network network) {
final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) {
final Pair<String, Network> perNetworkServiceType = clients.keyAt(i);
- if (isNetworkMatched(network, perNetworkServiceType.second)) {
+ final Network serviceTypeNetwork = perNetworkServiceType.second;
+ if (Objects.equals(network, serviceTypeNetwork)) {
list.add(clients.valueAt(i));
}
}
@@ -100,10 +109,36 @@
}
public MdnsDiscoveryManager(@NonNull ExecutorProvider executorProvider,
- @NonNull MdnsSocketClientBase socketClient) {
+ @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog) {
this.executorProvider = executorProvider;
this.socketClient = socketClient;
- perNetworkServiceTypeClients = new PerNetworkServiceTypeClients();
+ this.sharedLog = sharedLog;
+ this.perNetworkServiceTypeClients = new PerNetworkServiceTypeClients();
+ if (socketClient.getLooper() != null) {
+ this.handlerThread = null;
+ this.handler = new Handler(socketClient.getLooper());
+ } else {
+ this.handlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName());
+ this.handlerThread.start();
+ this.handler = new Handler(handlerThread.getLooper());
+ }
+ }
+
+ private void checkAndRunOnHandlerThread(@NonNull Runnable function) {
+ if (this.handlerThread == null) {
+ function.run();
+ } else {
+ handler.post(function);
+ }
+ }
+
+ /**
+ * Do the cleanup of the MdnsDiscoveryManager
+ */
+ public void shutDown() {
+ if (this.handlerThread != null) {
+ this.handlerThread.quitSafely();
+ }
}
/**
@@ -116,35 +151,57 @@
* serviceType}.
*/
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
- public synchronized void registerListener(
+ public void registerListener(
@NonNull String serviceType,
@NonNull MdnsServiceBrowserListener listener,
@NonNull MdnsSearchOptions searchOptions) {
- LOGGER.log(
- "Registering listener for subtypes: %s",
- TextUtils.join(",", searchOptions.getSubtypes()));
+ sharedLog.i("Registering listener for serviceType: " + serviceType);
+ checkAndRunOnHandlerThread(() ->
+ handleRegisterListener(serviceType, listener, searchOptions));
+ }
+
+ private void handleRegisterListener(
+ @NonNull String serviceType,
+ @NonNull MdnsServiceBrowserListener listener,
+ @NonNull MdnsSearchOptions searchOptions) {
if (perNetworkServiceTypeClients.isEmpty()) {
// First listener. Starts the socket client.
try {
socketClient.startDiscovery();
} catch (IOException e) {
- LOGGER.e("Failed to start discover.", e);
+ sharedLog.e("Failed to start discover.", e);
return;
}
}
// Request the network for discovery.
- socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork(), network -> {
- synchronized (this) {
- // All listeners of the same service types shares the same MdnsServiceTypeClient.
- MdnsServiceTypeClient serviceTypeClient =
- perNetworkServiceTypeClients.get(serviceType, network);
- if (serviceTypeClient == null) {
- serviceTypeClient = createServiceTypeClient(serviceType, network);
- perNetworkServiceTypeClients.put(serviceType, network, serviceTypeClient);
- }
- serviceTypeClient.startSendAndReceive(listener, searchOptions);
- }
- });
+ socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork(),
+ new MdnsSocketClientBase.SocketCreationCallback() {
+ @Override
+ public void onSocketCreated(@Nullable Network network) {
+ ensureRunningOnHandlerThread(handler);
+ // All listeners of the same service types shares the same
+ // MdnsServiceTypeClient.
+ MdnsServiceTypeClient serviceTypeClient =
+ perNetworkServiceTypeClients.get(serviceType, network);
+ if (serviceTypeClient == null) {
+ serviceTypeClient = createServiceTypeClient(serviceType, network);
+ perNetworkServiceTypeClients.put(serviceType, network,
+ serviceTypeClient);
+ }
+ serviceTypeClient.startSendAndReceive(listener, searchOptions);
+ }
+
+ @Override
+ public void onAllSocketsDestroyed(@Nullable Network network) {
+ ensureRunningOnHandlerThread(handler);
+ final MdnsServiceTypeClient serviceTypeClient =
+ perNetworkServiceTypeClients.get(serviceType, network);
+ if (serviceTypeClient == null) return;
+ // Notify all listeners that all services are removed from this socket.
+ serviceTypeClient.notifySocketDestroyed();
+ perNetworkServiceTypeClients.remove(serviceTypeClient);
+ }
+ });
}
/**
@@ -155,10 +212,14 @@
* @param listener The {@link MdnsServiceBrowserListener} listener.
*/
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
- public synchronized void unregisterListener(
+ public void unregisterListener(
@NonNull String serviceType, @NonNull MdnsServiceBrowserListener listener) {
- LOGGER.log("Unregistering listener for service type: %s", serviceType);
- if (DBG) Log.d(TAG, "Unregistering listener for serviceType:" + serviceType);
+ sharedLog.i("Unregistering listener for serviceType:" + serviceType);
+ checkAndRunOnHandlerThread(() -> handleUnregisterListener(serviceType, listener));
+ }
+
+ private void handleUnregisterListener(
+ @NonNull String serviceType, @NonNull MdnsServiceBrowserListener listener) {
final List<MdnsServiceTypeClient> serviceTypeClients =
perNetworkServiceTypeClients.getByServiceType(serviceType);
if (serviceTypeClients.isEmpty()) {
@@ -170,30 +231,50 @@
// No listener is registered for the service type anymore, remove it from the list
// of the service type clients.
perNetworkServiceTypeClients.remove(serviceTypeClient);
- if (perNetworkServiceTypeClients.isEmpty()) {
- // No discovery request. Stops the socket client.
- socketClient.stopDiscovery();
- }
}
}
+ if (perNetworkServiceTypeClients.isEmpty()) {
+ // No discovery request. Stops the socket client.
+ socketClient.stopDiscovery();
+ }
// Unrequested the network.
socketClient.notifyNetworkUnrequested(listener);
}
@Override
- public synchronized void onResponseReceived(@NonNull MdnsPacket packet,
- int interfaceIndex, Network network) {
+ public void onResponseReceived(@NonNull MdnsPacket packet,
+ int interfaceIndex, @Nullable Network network) {
+ checkAndRunOnHandlerThread(() ->
+ handleOnResponseReceived(packet, interfaceIndex, network));
+ }
+
+ private void handleOnResponseReceived(@NonNull MdnsPacket packet, int interfaceIndex,
+ @Nullable Network network) {
for (MdnsServiceTypeClient serviceTypeClient
- : perNetworkServiceTypeClients.getByMatchingNetwork(network)) {
+ : getMdnsServiceTypeClient(network)) {
serviceTypeClient.processResponse(packet, interfaceIndex, network);
}
}
+ private List<MdnsServiceTypeClient> getMdnsServiceTypeClient(@Nullable Network network) {
+ if (socketClient.supportsRequestingSpecificNetworks()) {
+ return perNetworkServiceTypeClients.getByNetwork(network);
+ } else {
+ return perNetworkServiceTypeClients.getByNetwork(null);
+ }
+ }
+
@Override
- public synchronized void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
- Network network) {
+ public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
+ @Nullable Network network) {
+ checkAndRunOnHandlerThread(() ->
+ handleOnFailedToParseMdnsResponse(receivedPacketNumber, errorCode, network));
+ }
+
+ private void handleOnFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
+ @Nullable Network network) {
for (MdnsServiceTypeClient serviceTypeClient
- : perNetworkServiceTypeClients.getByMatchingNetwork(network)) {
+ : getMdnsServiceTypeClient(network)) {
serviceTypeClient.onFailedToParseMdnsResponse(receivedPacketNumber, errorCode);
}
}
@@ -201,8 +282,10 @@
@VisibleForTesting
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@Nullable Network network) {
+ sharedLog.log("createServiceTypeClient for type:" + serviceType + ", net:" + network);
return new MdnsServiceTypeClient(
serviceType, socketClient,
- executorProvider.newServiceTypeClientSchedulerExecutor(), network);
+ executorProvider.newServiceTypeClientSchedulerExecutor(), network,
+ sharedLog.forSubComponent(serviceType + "-" + network));
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
index 79cddce..724a704 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiser.java
@@ -26,6 +26,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.HexDump;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo;
import com.android.server.connectivity.mdns.MdnsPacketRepeater.PacketRepeaterCallback;
@@ -62,6 +63,9 @@
@NonNull
private final MdnsReplySender mReplySender;
+ @NonNull
+ private final SharedLog mSharedLog;
+
/**
* Callbacks called by {@link MdnsInterfaceAdvertiser} to report status updates.
*/
@@ -96,15 +100,13 @@
@Override
public void onFinished(MdnsProber.ProbingInfo info) {
final MdnsAnnouncer.AnnouncementInfo announcementInfo;
- if (DBG) {
- Log.v(mTag, "Probing finished for service " + info.getServiceId());
- }
+ mSharedLog.i("Probing finished for service " + info.getServiceId());
mCbHandler.post(() -> mCb.onRegisterServiceSucceeded(
MdnsInterfaceAdvertiser.this, info.getServiceId()));
try {
announcementInfo = mRecordRepository.onProbingSucceeded(info);
} catch (IOException e) {
- Log.e(mTag, "Error building announcements", e);
+ mSharedLog.e("Error building announcements", e);
return;
}
@@ -168,28 +170,30 @@
}
}
- public MdnsInterfaceAdvertiser(@NonNull String logTag,
- @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> initialAddresses,
- @NonNull Looper looper, @NonNull byte[] packetCreationBuffer, @NonNull Callback cb,
- @NonNull String[] deviceHostName) {
- this(logTag, socket, initialAddresses, looper, packetCreationBuffer, cb,
- new Dependencies(), deviceHostName);
+ public MdnsInterfaceAdvertiser(@NonNull MdnsInterfaceSocket socket,
+ @NonNull List<LinkAddress> initialAddresses, @NonNull Looper looper,
+ @NonNull byte[] packetCreationBuffer, @NonNull Callback cb,
+ @NonNull String[] deviceHostName, @NonNull SharedLog sharedLog) {
+ this(socket, initialAddresses, looper, packetCreationBuffer, cb,
+ new Dependencies(), deviceHostName, sharedLog);
}
- public MdnsInterfaceAdvertiser(@NonNull String logTag,
- @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> initialAddresses,
- @NonNull Looper looper, @NonNull byte[] packetCreationBuffer, @NonNull Callback cb,
- @NonNull Dependencies deps, @NonNull String[] deviceHostName) {
- mTag = MdnsInterfaceAdvertiser.class.getSimpleName() + "/" + logTag;
+ public MdnsInterfaceAdvertiser(@NonNull MdnsInterfaceSocket socket,
+ @NonNull List<LinkAddress> initialAddresses, @NonNull Looper looper,
+ @NonNull byte[] packetCreationBuffer, @NonNull Callback cb, @NonNull Dependencies deps,
+ @NonNull String[] deviceHostName, @NonNull SharedLog sharedLog) {
+ mTag = MdnsInterfaceAdvertiser.class.getSimpleName() + "/" + sharedLog.getTag();
mRecordRepository = deps.makeRecordRepository(looper, deviceHostName);
mRecordRepository.updateAddresses(initialAddresses);
mSocket = socket;
mCb = cb;
mCbHandler = new Handler(looper);
- mReplySender = deps.makeReplySender(logTag, looper, socket, packetCreationBuffer);
- mAnnouncer = deps.makeMdnsAnnouncer(logTag, looper, mReplySender,
+ mReplySender = deps.makeReplySender(sharedLog.getTag(), looper, socket,
+ packetCreationBuffer);
+ mAnnouncer = deps.makeMdnsAnnouncer(sharedLog.getTag(), looper, mReplySender,
mAnnouncingCallback);
- mProber = deps.makeMdnsProber(logTag, looper, mReplySender, mProbingCallback);
+ mProber = deps.makeMdnsProber(sharedLog.getTag(), looper, mReplySender, mProbingCallback);
+ mSharedLog = sharedLog;
}
/**
@@ -208,15 +212,14 @@
*
* @throws NameConflictException There is already a service being advertised with that name.
*/
- public void addService(int id, NsdServiceInfo service) throws NameConflictException {
- final int replacedExitingService = mRecordRepository.addService(id, service);
+ public void addService(int id, NsdServiceInfo service, @Nullable String subtype)
+ throws NameConflictException {
+ final int replacedExitingService = mRecordRepository.addService(id, service, subtype);
// Cancel announcements for the existing service. This only happens for exiting services
// (so cancelling exiting announcements), as per RecordRepository.addService.
if (replacedExitingService >= 0) {
- if (DBG) {
- Log.d(mTag, "Service " + replacedExitingService
- + " getting re-added, cancelling exit announcements");
- }
+ mSharedLog.i("Service " + replacedExitingService
+ + " getting re-added, cancelling exit announcements");
mAnnouncer.stop(replacedExitingService);
}
mProber.startProbing(mRecordRepository.setServiceProbing(id));
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
index 31627f8..119c7a8 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsInterfaceSocket.java
@@ -78,7 +78,7 @@
}
mPacketReader = new MulticastPacketReader(networkInterface.getName(), mFileDescriptor,
- new Handler(looper), packetReadBuffer, port);
+ new Handler(looper), packetReadBuffer);
mPacketReader.start();
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index 7af2231..73e4497 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -16,8 +16,7 @@
package com.android.server.connectivity.mdns;
-import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
-import static com.android.server.connectivity.mdns.MdnsSocketProvider.isNetworkMatched;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +33,7 @@
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.List;
-import java.util.Map;
+import java.util.Objects;
/**
* The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
@@ -48,9 +47,8 @@
@NonNull private final Handler mHandler;
@NonNull private final MdnsSocketProvider mSocketProvider;
- private final Map<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
+ private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
new ArrayMap<>();
- private final ArrayMap<MdnsInterfaceSocket, Network> mActiveNetworkSockets = new ArrayMap<>();
private final ArrayMap<MdnsInterfaceSocket, ReadPacketHandler> mSocketPacketHandlers =
new ArrayMap<>();
private MdnsSocketClientBase.Callback mCallback = null;
@@ -63,7 +61,11 @@
}
private class InterfaceSocketCallback implements MdnsSocketProvider.SocketCallback {
+ @NonNull
private final SocketCreationCallback mSocketCreationCallback;
+ @NonNull
+ private final ArrayMap<MdnsInterfaceSocket, Network> mActiveNetworkSockets =
+ new ArrayMap<>();
InterfaceSocketCallback(SocketCreationCallback socketCreationCallback) {
mSocketCreationCallback = socketCreationCallback;
@@ -88,9 +90,59 @@
@Override
public void onInterfaceDestroyed(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket) {
- mSocketPacketHandlers.remove(socket);
- mActiveNetworkSockets.remove(socket);
+ notifySocketDestroyed(socket);
+ maybeCleanupPacketHandler(socket);
}
+
+ private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
+ final Network network = mActiveNetworkSockets.remove(socket);
+ if (!isAnySocketActive(network)) {
+ mSocketCreationCallback.onAllSocketsDestroyed(network);
+ }
+ }
+
+ void onNetworkUnrequested() {
+ for (int i = mActiveNetworkSockets.size() - 1; i >= 0; i--) {
+ // Iterate from the end so the socket can be removed
+ final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
+ notifySocketDestroyed(socket);
+ maybeCleanupPacketHandler(socket);
+ }
+ }
+ }
+
+ private boolean isSocketActive(@NonNull MdnsInterfaceSocket socket) {
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ if (isc.mActiveNetworkSockets.containsKey(socket)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isAnySocketActive(@Nullable Network network) {
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ if (isc.mActiveNetworkSockets.containsValue(network)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ArrayMap<MdnsInterfaceSocket, Network> getActiveSockets() {
+ final ArrayMap<MdnsInterfaceSocket, Network> sockets = new ArrayMap<>();
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ sockets.putAll(isc.mActiveNetworkSockets);
+ }
+ return sockets;
+ }
+
+ private void maybeCleanupPacketHandler(@NonNull MdnsInterfaceSocket socket) {
+ if (isSocketActive(socket)) return;
+ mSocketPacketHandlers.remove(socket);
}
private class ReadPacketHandler implements MulticastPacketReader.PacketHandler {
@@ -142,25 +194,42 @@
@Override
public void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) {
ensureRunningOnHandlerThread(mHandler);
- final InterfaceSocketCallback callback = mRequestedNetworks.remove(listener);
+ final InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
if (callback == null) {
Log.e(TAG, "Can not be unrequested with unknown listener=" + listener);
return;
}
+ callback.onNetworkUnrequested();
+ // onNetworkUnrequested does cleanups based on mRequestedNetworks, only remove afterwards
+ mRequestedNetworks.remove(listener);
mSocketProvider.unrequestSocket(callback);
}
+ @Override
+ public Looper getLooper() {
+ return mHandler.getLooper();
+ }
+
+ @Override
+ public boolean supportsRequestingSpecificNetworks() {
+ return true;
+ }
+
private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork) {
final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet6Address;
final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet4Address;
- for (int i = 0; i < mActiveNetworkSockets.size(); i++) {
- final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
- final Network network = mActiveNetworkSockets.valueAt(i);
+ final ArrayMap<MdnsInterfaceSocket, Network> activeSockets = getActiveSockets();
+ for (int i = 0; i < activeSockets.size(); i++) {
+ final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
+ final Network network = activeSockets.valueAt(i);
// Check ip capability and network before sending packet
if (((isIpv6 && socket.hasJoinedIpv6()) || (isIpv4 && socket.hasJoinedIpv4()))
- && isNetworkMatched(targetNetwork, network)) {
+ // Contrary to MdnsUtils.isNetworkMatched, only send packets targeting
+ // the null network to interfaces that have the null network (tethering
+ // downstream interfaces).
+ && Objects.equals(network, targetNetwork)) {
try {
socket.send(packet);
} catch (IOException e) {
@@ -192,12 +261,6 @@
}
}
- /** Sends a mDNS request packet that asks for multicast response. */
- @Override
- public void sendMulticastPacket(@NonNull DatagramPacket packet) {
- sendMulticastPacket(packet, null /* network */);
- }
-
/**
* Sends a mDNS request packet via given network that asks for multicast response. Null network
* means sending packet via all networks.
@@ -207,12 +270,6 @@
mHandler.post(() -> sendMdnsPacket(packet, network));
}
- /** Sends a mDNS request packet that asks for unicast response. */
- @Override
- public void sendUnicastPacket(@NonNull DatagramPacket packet) {
- sendUnicastPacket(packet, null /* network */);
- }
-
/**
* Sends a mDNS request packet via given network that asks for unicast response. Null network
* means sending packet via all networks.
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
index 6ec2f99..1239180 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
@@ -20,6 +20,7 @@
import android.text.TextUtils;
import com.android.net.module.util.CollectionUtils;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -152,7 +153,8 @@
@Override
public int hashCode() {
return Objects.hash(super.hashCode(),
- Arrays.hashCode(mNextDomain), Arrays.hashCode(mTypes));
+ Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(mNextDomain)),
+ Arrays.hashCode(mTypes));
}
@Override
@@ -165,7 +167,8 @@
}
return super.equals(other)
- && Arrays.equals(mNextDomain, ((MdnsNsecRecord) other).mNextDomain)
+ && MdnsUtils.equalsDnsLabelIgnoreDnsCase(mNextDomain,
+ ((MdnsNsecRecord) other).mNextDomain)
&& Arrays.equals(mTypes, ((MdnsNsecRecord) other).mTypes);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
index c0f9b8b..6879a64 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
@@ -17,11 +17,11 @@
package com.android.server.connectivity.mdns;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.SocketAddress;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -180,7 +180,7 @@
int existingOffset = entry.getKey();
String[] existingLabels = entry.getValue();
- if (Arrays.equals(existingLabels, labels)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(existingLabels, labels)) {
writePointer(existingOffset);
return;
} else if (MdnsRecord.labelsAreSuffix(existingLabels, labels)) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
index 2c7b26b..c88ead0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -60,7 +61,8 @@
}
public boolean hasSubtype() {
- return (name != null) && (name.length > 2) && name[1].equals(MdnsConstants.SUBTYPE_LABEL);
+ return (name != null) && (name.length > 2) && MdnsUtils.equalsIgnoreDnsCase(name[1],
+ MdnsConstants.SUBTYPE_LABEL);
}
public String getSubtype() {
@@ -74,7 +76,7 @@
@Override
public int hashCode() {
- return (super.hashCode() * 31) + Arrays.hashCode(pointer);
+ return (super.hashCode() * 31) + Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(pointer));
}
@Override
@@ -86,6 +88,7 @@
return false;
}
- return super.equals(other) && Arrays.equals(pointer, ((MdnsPointerRecord) other).pointer);
+ return super.equals(other) && MdnsUtils.equalsDnsLabelIgnoreDnsCase(pointer,
+ ((MdnsPointerRecord) other).pointer);
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
index 669b323..ecf846e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
@@ -21,9 +21,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -113,7 +113,8 @@
*/
private static boolean containsName(@NonNull List<MdnsRecord> records,
@NonNull String[] name) {
- return CollectionUtils.any(records, r -> Arrays.equals(name, r.getName()));
+ return CollectionUtils.any(records,
+ r -> MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, r.getName()));
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
index bcee9d1..28bd1b4 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -46,6 +47,8 @@
public static final long RECEIPT_TIME_NOT_SENT = 0L;
public static final int CLASS_ANY = 0x00ff;
+ /** Max label length as per RFC 1034/1035 */
+ public static final int MAX_LABEL_LENGTH = 63;
/** Status indicating that the record is current. */
public static final int STATUS_OK = 0;
@@ -134,7 +137,7 @@
}
for (int i = 0; i < list1.length; ++i) {
- if (!list1[i].equals(list2[i + offset])) {
+ if (!MdnsUtils.equalsIgnoreDnsCase(list1[i], list2[i + offset])) {
return false;
}
}
@@ -269,12 +272,13 @@
MdnsRecord otherRecord = (MdnsRecord) other;
- return Arrays.equals(name, otherRecord.name) && (type == otherRecord.type);
+ return MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, otherRecord.name) && (type
+ == otherRecord.type);
}
@Override
public int hashCode() {
- return Objects.hash(Arrays.hashCode(name), type);
+ return Objects.hash(Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(name)), type);
}
/**
@@ -295,7 +299,7 @@
public Key(int recordType, String[] recordName) {
this.recordType = recordType;
- this.recordName = recordName;
+ this.recordName = MdnsUtils.toDnsLabelsLowerCase(recordName);
}
@Override
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index 1329172..1375279 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -30,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.HexDump;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.Inet4Address;
@@ -69,6 +70,8 @@
// Top-level domain for link-local queries, as per RFC6762 3.
private static final String LOCAL_TLD = "local";
+ // Subtype separator as per RFC6763 7.1 (_printer._sub._http._tcp.local)
+ private static final String SUBTYPE_SEPARATOR = "_sub";
// Service type for service enumeration (RFC6763 9.)
private static final String[] DNS_SD_SERVICE_TYPE =
@@ -156,13 +159,15 @@
@NonNull
public final List<RecordInfo<?>> allRecords;
@NonNull
- public final RecordInfo<MdnsPointerRecord> ptrRecord;
+ public final List<RecordInfo<MdnsPointerRecord>> ptrRecords;
@NonNull
public final RecordInfo<MdnsServiceRecord> srvRecord;
@NonNull
public final RecordInfo<MdnsTextRecord> txtRecord;
@NonNull
public final NsdServiceInfo serviceInfo;
+ @Nullable
+ public final String subtype;
/**
* Whether the service is sending exit announcements and will be destroyed soon.
@@ -175,14 +180,16 @@
* @param deviceHostname Hostname of the device (for the interface used)
* @param serviceInfo Service to advertise
*/
- ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo) {
+ ServiceRegistration(@NonNull String[] deviceHostname, @NonNull NsdServiceInfo serviceInfo,
+ @Nullable String subtype) {
this.serviceInfo = serviceInfo;
+ this.subtype = subtype;
final String[] serviceType = splitServiceType(serviceInfo);
final String[] serviceName = splitFullyQualifiedName(serviceInfo, serviceType);
// Service PTR record
- ptrRecord = new RecordInfo<>(
+ final RecordInfo<MdnsPointerRecord> ptrRecord = new RecordInfo<>(
serviceInfo,
new MdnsPointerRecord(
serviceType,
@@ -192,6 +199,26 @@
serviceName),
true /* sharedName */, true /* probing */);
+ if (subtype == null) {
+ this.ptrRecords = Collections.singletonList(ptrRecord);
+ } else {
+ final String[] subtypeName = new String[serviceType.length + 2];
+ System.arraycopy(serviceType, 0, subtypeName, 2, serviceType.length);
+ subtypeName[0] = subtype;
+ subtypeName[1] = SUBTYPE_SEPARATOR;
+ final RecordInfo<MdnsPointerRecord> subtypeRecord = new RecordInfo<>(
+ serviceInfo,
+ new MdnsPointerRecord(
+ subtypeName,
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ NON_NAME_RECORDS_TTL_MILLIS,
+ serviceName),
+ true /* sharedName */, true /* probing */);
+
+ this.ptrRecords = List.of(ptrRecord, subtypeRecord);
+ }
+
srvRecord = new RecordInfo<>(
serviceInfo,
new MdnsServiceRecord(serviceName,
@@ -211,8 +238,8 @@
attrsToTextEntries(serviceInfo.getAttributes())),
false /* sharedName */, true /* probing */);
- final ArrayList<RecordInfo<?>> allRecords = new ArrayList<>(4);
- allRecords.add(ptrRecord);
+ final ArrayList<RecordInfo<?>> allRecords = new ArrayList<>(5);
+ allRecords.addAll(ptrRecords);
allRecords.add(srvRecord);
allRecords.add(txtRecord);
// Service type enumeration record (RFC6763 9.)
@@ -275,7 +302,8 @@
* ID of the replaced service.
* @throws NameConflictException There is already a (non-exiting) service using the name.
*/
- public int addService(int serviceId, NsdServiceInfo serviceInfo) throws NameConflictException {
+ public int addService(int serviceId, NsdServiceInfo serviceInfo, @Nullable String subtype)
+ throws NameConflictException {
if (mServices.contains(serviceId)) {
throw new IllegalArgumentException(
"Service ID must not be reused across registrations: " + serviceId);
@@ -288,7 +316,7 @@
}
final ServiceRegistration registration = new ServiceRegistration(
- mDeviceHostname, serviceInfo);
+ mDeviceHostname, serviceInfo, subtype);
mServices.put(serviceId, registration);
// Remove existing exiting service
@@ -302,7 +330,8 @@
private int getServiceByName(@NonNull String serviceName) {
for (int i = 0; i < mServices.size(); i++) {
final ServiceRegistration registration = mServices.valueAt(i);
- if (serviceName.equals(registration.serviceInfo.getServiceName())) {
+ if (MdnsUtils.equalsIgnoreDnsCase(serviceName,
+ registration.serviceInfo.getServiceName())) {
return mServices.keyAt(i);
}
}
@@ -344,24 +373,25 @@
if (registration == null) return null;
if (registration.exiting) return null;
- // Send exit (TTL 0) for the PTR record, if the record was sent (in particular don't send
+ // Send exit (TTL 0) for the PTR records, if at least one was sent (in particular don't send
// if still probing)
- if (registration.ptrRecord.lastSentTimeMs == 0L) {
+ if (CollectionUtils.all(registration.ptrRecords, r -> r.lastSentTimeMs == 0L)) {
return null;
}
registration.exiting = true;
- final MdnsPointerRecord expiredRecord = new MdnsPointerRecord(
- registration.ptrRecord.record.getName(),
- 0L /* receiptTimeMillis */,
- true /* cacheFlush */,
- 0L /* ttlMillis */,
- registration.ptrRecord.record.getPointer());
+ final List<MdnsRecord> expiredRecords = CollectionUtils.map(registration.ptrRecords,
+ r -> new MdnsPointerRecord(
+ r.record.getName(),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 0L /* ttlMillis */,
+ r.record.getPointer()));
// Exit should be skipped if the record is still advertised by another service, but that
// would be a conflict (2 service registrations with the same service name), so it would
// not have been allowed by the repository.
- return new MdnsAnnouncer.ExitAnnouncementInfo(id, Collections.singletonList(expiredRecord));
+ return new MdnsAnnouncer.ExitAnnouncementInfo(id, expiredRecords);
}
public void removeService(int id) {
@@ -442,7 +472,7 @@
for (int i = 0; i < mServices.size(); i++) {
final ServiceRegistration registration = mServices.valueAt(i);
if (registration.exiting) continue;
- addReplyFromService(question, registration.allRecords, registration.ptrRecord,
+ addReplyFromService(question, registration.allRecords, registration.ptrRecords,
registration.srvRecord, registration.txtRecord, replyUnicast, now,
answerInfo, additionalAnswerRecords);
}
@@ -499,7 +529,7 @@
*/
private void addReplyFromService(@NonNull MdnsRecord question,
@NonNull List<RecordInfo<?>> serviceRecords,
- @Nullable RecordInfo<MdnsPointerRecord> servicePtrRecord,
+ @Nullable List<RecordInfo<MdnsPointerRecord>> servicePtrRecords,
@Nullable RecordInfo<MdnsServiceRecord> serviceSrvRecord,
@Nullable RecordInfo<MdnsTextRecord> serviceTxtRecord,
boolean replyUnicast, long now, @NonNull List<RecordInfo<?>> answerInfo,
@@ -517,7 +547,9 @@
must match the question qtype unless the qtype is "ANY" (255) or the rrtype is
"CNAME" (5), and the record rrclass must match the question qclass unless the
qclass is "ANY" (255) */
- if (!Arrays.equals(info.record.getName(), question.getName())) continue;
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(info.record.getName(), question.getName())) {
+ continue;
+ }
hasFullyOwnedNameMatch |= !info.isSharedName;
// The repository does not store CNAME records
@@ -531,7 +563,8 @@
}
hasKnownAnswer = true;
- hasDnsSdPtrRecordAnswer |= (info == servicePtrRecord);
+ hasDnsSdPtrRecordAnswer |= (servicePtrRecords != null
+ && CollectionUtils.any(servicePtrRecords, r -> info == r));
hasDnsSdSrvRecordAnswer |= (info == serviceSrvRecord);
// TODO: responses to probe queries should bypass this check and only ensure the
@@ -718,7 +751,10 @@
// happens. In fact probing is only done for the service name in the SRV record.
// This means only SRV and TXT records need to be checked.
final RecordInfo<MdnsServiceRecord> srvRecord = registration.srvRecord;
- if (!Arrays.equals(record.getName(), srvRecord.record.getName())) continue;
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(),
+ srvRecord.record.getName())) {
+ continue;
+ }
// As per RFC6762 9., it's fine if the "conflict" is an identical record with same
// data.
@@ -791,10 +827,11 @@
*/
@Nullable
public MdnsProber.ProbingInfo renameServiceForConflict(int serviceId, NsdServiceInfo newInfo) {
- if (!mServices.contains(serviceId)) return null;
+ final ServiceRegistration existing = mServices.get(serviceId);
+ if (existing == null) return null;
final ServiceRegistration newService = new ServiceRegistration(
- mDeviceHostname, newInfo);
+ mDeviceHostname, newInfo, existing.subtype);
mServices.put(serviceId, newService);
return makeProbingInfo(serviceId, newService.srvRecord.record);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
index f1389ca..8bc598d 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsReplySender.java
@@ -16,7 +16,7 @@
package com.android.server.connectivity.mdns;
-import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.annotation.NonNull;
import android.os.Handler;
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
index e0100f6..28aa640 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -21,10 +21,10 @@
import android.net.Network;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@@ -103,7 +103,7 @@
* pointer record is already present in the response with the same TTL.
*/
public synchronized boolean addPointerRecord(MdnsPointerRecord pointerRecord) {
- if (!Arrays.equals(serviceName, pointerRecord.getPointer())) {
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceName, pointerRecord.getPointer())) {
throw new IllegalArgumentException(
"Pointer records for different service names cannot be added");
}
@@ -301,13 +301,13 @@
boolean dropAddressRecords = false;
for (MdnsInetAddressRecord inetAddressRecord : getInet4AddressRecords()) {
- if (!Arrays.equals(
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
}
for (MdnsInetAddressRecord inetAddressRecord : getInet6AddressRecords()) {
- if (!Arrays.equals(
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 129db7e..77b5c58 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -23,10 +23,10 @@
import android.util.ArraySet;
import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.EOFException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -50,7 +50,7 @@
List<MdnsResponse> responses, String[] pointer) {
if (responses != null) {
for (MdnsResponse response : responses) {
- if (Arrays.equals(response.getServiceName(), pointer)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(response.getServiceName(), pointer)) {
return response;
}
}
@@ -66,7 +66,8 @@
if (serviceRecord == null) {
continue;
}
- if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(),
+ hostName)) {
return response;
}
}
@@ -164,11 +165,8 @@
for (MdnsRecord record : records) {
if (record instanceof MdnsPointerRecord) {
String[] name = record.getName();
- if ((serviceType == null)
- || Arrays.equals(name, serviceType)
- || ((name.length == (serviceType.length + 2))
- && name[1].equals(MdnsConstants.SUBTYPE_LABEL)
- && MdnsRecord.labelsAreSuffix(serviceType, name))) {
+ if ((serviceType == null) || MdnsUtils.typeEqualsOrIsSubtype(
+ serviceType, name)) {
MdnsPointerRecord pointerRecord = (MdnsPointerRecord) record;
// Group PTR records that refer to the same service instance name into a single
// response.
@@ -296,7 +294,7 @@
if (serviceRecord == null) {
continue;
}
- if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(), hostName)) {
if (result == null) {
result = new ArrayList<>(/* initialCapacity= */ responses.size());
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
new file mode 100644
index 0000000..cd0be67
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLowerCase;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The {@link MdnsServiceCache} manages the service which discovers from each socket and cache these
+ * services to reduce duplicated queries.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the looper thread.
+ * However, the constructor is an exception, as it is called on another thread;
+ * therefore for thread safety all members of this class MUST either be final or initialized
+ * to their default value (0, false or null).
+ */
+public class MdnsServiceCache {
+ private static class CacheKey {
+ @NonNull final String mLowercaseServiceType;
+ @Nullable final Network mNetwork;
+
+ CacheKey(@NonNull String serviceType, @Nullable Network network) {
+ mLowercaseServiceType = toDnsLowerCase(serviceType);
+ mNetwork = network;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(mLowercaseServiceType, mNetwork);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof CacheKey)) {
+ return false;
+ }
+ return Objects.equals(mLowercaseServiceType, ((CacheKey) other).mLowercaseServiceType)
+ && Objects.equals(mNetwork, ((CacheKey) other).mNetwork);
+ }
+ }
+ /**
+ * A map of cached services. Key is composed of service name, type and network. Value is the
+ * service which use the service type to discover from each socket.
+ */
+ @NonNull
+ private final ArrayMap<CacheKey, List<MdnsResponse>> mCachedServices = new ArrayMap<>();
+ @NonNull
+ private final Handler mHandler;
+
+ public MdnsServiceCache(@NonNull Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * Get the cache services which are queried from given service type and network.
+ *
+ * @param serviceType the target service type.
+ * @param network the target network
+ * @return the set of services which matches the given service type.
+ */
+ @NonNull
+ public List<MdnsResponse> getCachedServices(@NonNull String serviceType,
+ @Nullable Network network) {
+ ensureRunningOnHandlerThread(mHandler);
+ final CacheKey key = new CacheKey(serviceType, network);
+ return mCachedServices.containsKey(key)
+ ? Collections.unmodifiableList(new ArrayList<>(mCachedServices.get(key)))
+ : Collections.emptyList();
+ }
+
+ private MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses,
+ @NonNull String serviceName) {
+ for (MdnsResponse response : responses) {
+ if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) {
+ return response;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the cache service.
+ *
+ * @param serviceName the target service name.
+ * @param serviceType the target service type.
+ * @param network the target network
+ * @return the service which matches given conditions.
+ */
+ @Nullable
+ public MdnsResponse getCachedService(@NonNull String serviceName,
+ @NonNull String serviceType, @Nullable Network network) {
+ ensureRunningOnHandlerThread(mHandler);
+ final List<MdnsResponse> responses =
+ mCachedServices.get(new CacheKey(serviceType, network));
+ if (responses == null) {
+ return null;
+ }
+ final MdnsResponse response = findMatchedResponse(responses, serviceName);
+ return response != null ? new MdnsResponse(response) : null;
+ }
+
+ /**
+ * Add or update a service.
+ *
+ * @param serviceType the service type.
+ * @param network the target network
+ * @param response the response of the discovered service.
+ */
+ public void addOrUpdateService(@NonNull String serviceType, @Nullable Network network,
+ @NonNull MdnsResponse response) {
+ ensureRunningOnHandlerThread(mHandler);
+ final List<MdnsResponse> responses = mCachedServices.computeIfAbsent(
+ new CacheKey(serviceType, network), key -> new ArrayList<>());
+ // Remove existing service if present.
+ final MdnsResponse existing =
+ findMatchedResponse(responses, response.getServiceInstanceName());
+ responses.remove(existing);
+ responses.add(response);
+ }
+
+ /**
+ * Remove a service which matches the given service name, type and network.
+ *
+ * @param serviceName the target service name.
+ * @param serviceType the target service type.
+ * @param network the target network.
+ */
+ @Nullable
+ public MdnsResponse removeService(@NonNull String serviceName, @NonNull String serviceType,
+ @Nullable Network network) {
+ ensureRunningOnHandlerThread(mHandler);
+ final List<MdnsResponse> responses =
+ mCachedServices.get(new CacheKey(serviceType, network));
+ if (responses == null) {
+ return null;
+ }
+ final Iterator<MdnsResponse> iterator = responses.iterator();
+ while (iterator.hasNext()) {
+ final MdnsResponse response = iterator.next();
+ if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) {
+ iterator.remove();
+ return response;
+ }
+ }
+ return null;
+ }
+
+ // TODO: check ttl expiration for each service and notify to the clients.
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
index ebd8b77..f851b35 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -142,7 +143,8 @@
@Override
public int hashCode() {
return (super.hashCode() * 31)
- + Objects.hash(servicePriority, serviceWeight, Arrays.hashCode(serviceHost),
+ + Objects.hash(servicePriority, serviceWeight,
+ Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(serviceHost)),
servicePort);
}
@@ -159,7 +161,7 @@
return super.equals(other)
&& (servicePriority == otherRecord.servicePriority)
&& (serviceWeight == otherRecord.serviceWeight)
- && Arrays.equals(serviceHost, otherRecord.serviceHost)
+ && MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceHost, otherRecord.serviceHost)
&& (servicePort == otherRecord.servicePort);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index 5298aef..c2c0db2 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -28,13 +28,16 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -49,8 +52,6 @@
public class MdnsServiceTypeClient {
private static final int DEFAULT_MTU = 1500;
- private static final MdnsLogger LOGGER = new MdnsLogger("MdnsServiceTypeClient");
-
private final String serviceType;
private final String[] serviceTypeLabels;
@@ -58,6 +59,7 @@
private final MdnsResponseDecoder responseDecoder;
private final ScheduledExecutorService executor;
@Nullable private final Network network;
+ @NonNull private final SharedLog sharedLog;
private final Object lock = new Object();
private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners =
new ArrayMap<>();
@@ -90,8 +92,10 @@
@NonNull String serviceType,
@NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor,
- @Nullable Network network) {
- this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), network);
+ @Nullable Network network,
+ @NonNull SharedLog sharedLog) {
+ this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), network,
+ sharedLog);
}
@VisibleForTesting
@@ -100,7 +104,8 @@
@NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor,
@NonNull MdnsResponseDecoder.Clock clock,
- @Nullable Network network) {
+ @Nullable Network network,
+ @NonNull SharedLog sharedLog) {
this.serviceType = serviceType;
this.socketClient = socketClient;
this.executor = executor;
@@ -108,6 +113,7 @@
this.responseDecoder = new MdnsResponseDecoder(clock, serviceTypeLabels);
this.clock = clock;
this.network = network;
+ this.sharedLog = sharedLog;
}
private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
@@ -172,6 +178,7 @@
@NonNull MdnsSearchOptions searchOptions) {
synchronized (lock) {
this.searchOptions = searchOptions;
+ boolean hadReply = false;
if (listeners.put(listener, searchOptions) == null) {
for (MdnsResponse existingResponse : instanceNameToResponse.values()) {
if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
@@ -180,6 +187,7 @@
listener.onServiceNameDiscovered(info);
if (existingResponse.isComplete()) {
listener.onServiceFound(info);
+ hadReply = true;
}
}
}
@@ -189,22 +197,37 @@
}
// Keep tracking the ScheduledFuture for the task so we can cancel it if caller is not
// interested anymore.
- requestTaskFuture =
- executor.submit(
- new QueryTask(
- new QueryTaskConfig(
- searchOptions.getSubtypes(),
- searchOptions.isPassiveMode(),
- ++currentSessionId,
- searchOptions.getNetwork())));
+ final QueryTaskConfig taskConfig = new QueryTaskConfig(
+ searchOptions.getSubtypes(),
+ searchOptions.isPassiveMode(),
+ ++currentSessionId,
+ network);
+ if (hadReply) {
+ requestTaskFuture = scheduleNextRunLocked(taskConfig);
+ } else {
+ requestTaskFuture = executor.submit(new QueryTask(taskConfig));
+ }
}
}
private boolean responseMatchesOptions(@NonNull MdnsResponse response,
@NonNull MdnsSearchOptions options) {
- if (options.getResolveInstanceName() == null) return true;
- // DNS is case-insensitive, so ignore case in the comparison
- return options.getResolveInstanceName().equalsIgnoreCase(response.getServiceInstanceName());
+ final boolean matchesInstanceName = options.getResolveInstanceName() == null
+ // DNS is case-insensitive, so ignore case in the comparison
+ || MdnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(),
+ response.getServiceInstanceName());
+
+ // If discovery is requiring some subtypes, the response must have one that matches a
+ // requested one.
+ final List<String> responseSubtypes = response.getSubtypes() == null
+ ? Collections.emptyList() : response.getSubtypes();
+ final boolean matchesSubtype = options.getSubtypes().size() == 0
+ || CollectionUtils.any(options.getSubtypes(), requiredSub ->
+ CollectionUtils.any(responseSubtypes, actualSub ->
+ MdnsUtils.equalsIgnoreDnsCase(
+ MdnsConstants.SUBTYPE_PREFIX + requiredSub, actualSub)));
+
+ return matchesInstanceName && matchesSubtype;
}
/**
@@ -260,21 +283,48 @@
}
}
+ /** Notify all services are removed because the socket is destroyed. */
+ public void notifySocketDestroyed() {
+ synchronized (lock) {
+ for (MdnsResponse response : instanceNameToResponse.values()) {
+ final String name = response.getServiceInstanceName();
+ if (name == null) continue;
+ for (int i = 0; i < listeners.size(); i++) {
+ if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
+ final MdnsServiceBrowserListener listener = listeners.keyAt(i);
+ final MdnsServiceInfo serviceInfo =
+ buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
+ if (response.isComplete()) {
+ sharedLog.log("Socket destroyed. onServiceRemoved: " + name);
+ listener.onServiceRemoved(serviceInfo);
+ }
+ sharedLog.log("Socket destroyed. onServiceNameRemoved: " + name);
+ listener.onServiceNameRemoved(serviceInfo);
+ }
+ }
+
+ if (requestTaskFuture != null) {
+ requestTaskFuture.cancel(true);
+ requestTaskFuture = null;
+ }
+ }
+ }
+
private void onResponseModified(@NonNull MdnsResponse response) {
+ final String serviceInstanceName = response.getServiceInstanceName();
final MdnsResponse currentResponse =
- instanceNameToResponse.get(response.getServiceInstanceName());
+ instanceNameToResponse.get(serviceInstanceName);
boolean newServiceFound = false;
boolean serviceBecomesComplete = false;
if (currentResponse == null) {
newServiceFound = true;
- String serviceInstanceName = response.getServiceInstanceName();
if (serviceInstanceName != null) {
instanceNameToResponse.put(serviceInstanceName, response);
}
} else {
boolean before = currentResponse.isComplete();
- instanceNameToResponse.put(response.getServiceInstanceName(), response);
+ instanceNameToResponse.put(serviceInstanceName, response);
boolean after = response.isComplete();
serviceBecomesComplete = !before && after;
}
@@ -285,13 +335,16 @@
if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
if (newServiceFound) {
+ sharedLog.log("onServiceNameDiscovered: " + serviceInfo);
listener.onServiceNameDiscovered(serviceInfo);
}
if (response.isComplete()) {
if (newServiceFound || serviceBecomesComplete) {
+ sharedLog.log("onServiceFound: " + serviceInfo);
listener.onServiceFound(serviceInfo);
} else {
+ sharedLog.log("onServiceUpdated: " + serviceInfo);
listener.onServiceUpdated(serviceInfo);
}
}
@@ -309,8 +362,10 @@
final MdnsServiceInfo serviceInfo =
buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
if (response.isComplete()) {
+ sharedLog.log("onServiceRemoved: " + serviceInfo);
listener.onServiceRemoved(serviceInfo);
}
+ sharedLog.log("onServiceNameRemoved: " + serviceInfo);
listener.onServiceNameRemoved(serviceInfo);
}
}
@@ -485,7 +540,7 @@
servicesToResolve)
.call();
} catch (RuntimeException e) {
- LOGGER.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
+ sharedLog.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
TextUtils.join(",", config.subtypes)), e);
result = null;
}
@@ -527,26 +582,31 @@
continue;
}
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
- String serviceInstanceName =
- existingResponse.getServiceInstanceName();
- if (serviceInstanceName != null) {
+ if (existingResponse.getServiceInstanceName() != null) {
final MdnsServiceInfo serviceInfo =
buildMdnsServiceInfoFromResponse(
existingResponse, serviceTypeLabels);
if (existingResponse.isComplete()) {
+ sharedLog.log("TTL expired. onServiceRemoved: "
+ + serviceInfo);
listener.onServiceRemoved(serviceInfo);
}
+ sharedLog.log("TTL expired. onServiceNameRemoved: "
+ + serviceInfo);
listener.onServiceNameRemoved(serviceInfo);
}
}
}
}
}
- QueryTaskConfig config = this.config.getConfigForNextRun();
- requestTaskFuture =
- executor.schedule(
- new QueryTask(config), config.timeToRunNextTaskInMs, MILLISECONDS);
+ requestTaskFuture = scheduleNextRunLocked(this.config);
}
}
}
+
+ @NonNull
+ private Future<?> scheduleNextRunLocked(@NonNull QueryTaskConfig lastRunConfig) {
+ QueryTaskConfig config = lastRunConfig.getConfigForNextRun();
+ return executor.schedule(new QueryTask(config), config.timeToRunNextTaskInMs, MILLISECONDS);
+ }
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
index 783b18a..b982644 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java
@@ -195,13 +195,11 @@
}
/** Sends a mDNS request packet that asks for multicast response. */
- @Override
public void sendMulticastPacket(@NonNull DatagramPacket packet) {
sendMdnsPacket(packet, multicastPacketQueue);
}
/** Sends a mDNS request packet that asks for unicast response. */
- @Override
public void sendUnicastPacket(DatagramPacket packet) {
if (useSeparateSocketForUnicast) {
sendMdnsPacket(packet, unicastPacketQueue);
@@ -210,6 +208,41 @@
}
}
+ @Override
+ public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ if (network != null) {
+ throw new IllegalArgumentException("This socket client does not support sending to "
+ + "specific networks");
+ }
+ sendMulticastPacket(packet);
+ }
+
+ @Override
+ public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
+ if (network != null) {
+ throw new IllegalArgumentException("This socket client does not support sending to "
+ + "specific networks");
+ }
+ sendUnicastPacket(packet);
+ }
+
+ @Override
+ public void notifyNetworkRequested(
+ @NonNull MdnsServiceBrowserListener listener,
+ @Nullable Network network,
+ @NonNull SocketCreationCallback socketCreationCallback) {
+ if (network != null) {
+ throw new IllegalArgumentException("This socket client does not support requesting "
+ + "specific networks");
+ }
+ socketCreationCallback.onSocketCreated(null);
+ }
+
+ @Override
+ public boolean supportsRequestingSpecificNetworks() {
+ return false;
+ }
+
private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse) {
if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) {
LOGGER.w("sendMdnsPacket() is called after discovery already stopped");
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
index ebafc49..e0762f9 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
+import android.os.Looper;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -38,39 +39,37 @@
/*** Set callback for receiving mDns response */
void setCallback(@Nullable Callback callback);
- /*** Sends a mDNS request packet that asks for multicast response. */
- void sendMulticastPacket(@NonNull DatagramPacket packet);
+ /**
+ * Send a mDNS request packet via given network that asks for multicast response.
+ *
+ * <p>The socket client may use a null network to identify some or all interfaces, in which case
+ * passing null sends the packet to these.
+ */
+ void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network);
/**
- * Sends a mDNS request packet via given network that asks for multicast response. Null network
- * means sending packet via all networks.
+ * Send a mDNS request packet via given network that asks for unicast response.
+ *
+ * <p>The socket client may use a null network to identify some or all interfaces, in which case
+ * passing null sends the packet to these.
*/
- default void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
- throw new UnsupportedOperationException(
- "This socket client doesn't support per-network sending");
- }
-
- /*** Sends a mDNS request packet that asks for unicast response. */
- void sendUnicastPacket(@NonNull DatagramPacket packet);
-
- /**
- * Sends a mDNS request packet via given network that asks for unicast response. Null network
- * means sending packet via all networks.
- */
- default void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) {
- throw new UnsupportedOperationException(
- "This socket client doesn't support per-network sending");
- }
+ void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network);
/*** Notify that the given network is requested for mdns discovery / resolution */
- default void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
- @Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback) {
- socketCreationCallback.onSocketCreated(network);
- }
+ void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
+ @Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback);
/*** Notify that the network is unrequested */
default void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) { }
+ /*** Gets looper that used by the socket client */
+ default Looper getLooper() {
+ return null;
+ }
+
+ /** Returns whether the socket client support requesting per network */
+ boolean supportsRequestingSpecificNetworks();
+
/*** Callback for mdns response */
interface Callback {
/*** Receive a mdns response */
@@ -86,5 +85,8 @@
interface SocketCreationCallback {
/*** Notify requested socket is created */
void onSocketCreated(@Nullable Network network);
+
+ /*** Notify requested socket is destroyed */
+ void onAllSocketsDestroyed(@Nullable Network network);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 8017ee0..2fa1ae4 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -20,6 +20,9 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.isNetworkMatched;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -42,7 +45,6 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.SharedLog;
-import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException;
import java.net.NetworkInterface;
@@ -66,7 +68,6 @@
// But 1440 should generally be enough because of standard Ethernet.
// Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
private static final int READ_BUFFER_SIZE = 2048;
- private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
private static final int IFACE_IDX_NOT_EXIST = -1;
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@@ -74,7 +75,8 @@
@NonNull private final Dependencies mDependencies;
@NonNull private final NetworkCallback mNetworkCallback;
@NonNull private final TetheringEventCallback mTetheringEventCallback;
- @NonNull private final ISocketNetLinkMonitor mSocketNetlinkMonitor;
+ @NonNull private final AbstractSocketNetlink mSocketNetlinkMonitor;
+ @NonNull private final SharedLog mSharedLog;
private final ArrayMap<Network, SocketInfo> mNetworkSockets = new ArrayMap<>();
private final ArrayMap<String, SocketInfo> mTetherInterfaceSockets = new ArrayMap<>();
private final ArrayMap<Network, LinkProperties> mActiveNetworksLinkProperties =
@@ -91,16 +93,18 @@
private boolean mMonitoringSockets = false;
private boolean mRequestStop = false;
- public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper) {
- this(context, looper, new Dependencies());
+ public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
+ @NonNull SharedLog sharedLog) {
+ this(context, looper, new Dependencies(), sharedLog);
}
MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
- @NonNull Dependencies deps) {
+ @NonNull Dependencies deps, @NonNull SharedLog sharedLog) {
mContext = context;
mLooper = looper;
mHandler = new Handler(looper);
mDependencies = deps;
+ mSharedLog = sharedLog;
mNetworkCallback = new NetworkCallback() {
@Override
public void onLost(Network network) {
@@ -132,8 +136,8 @@
}
};
- mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler, LOGGER.mLog,
- new NetLinkMessageProcessor());
+ mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler,
+ mSharedLog.forSubComponent("NetlinkMonitor"), new NetLinkMessageProcessor());
}
/**
@@ -171,7 +175,7 @@
return iface.getIndex();
}
/*** Creates a SocketNetlinkMonitor */
- public ISocketNetLinkMonitor createSocketNetlinkMonitor(@NonNull final Handler handler,
+ public AbstractSocketNetlink createSocketNetlinkMonitor(@NonNull final Handler handler,
@NonNull final SharedLog log,
@NonNull final NetLinkMonitorCallBack cb) {
return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
@@ -242,14 +246,6 @@
}
}
- /*** Ensure that current running thread is same as given handler thread */
- public static void ensureRunningOnHandlerThread(Handler handler) {
- if (handler.getLooper().getThread() != Thread.currentThread()) {
- throw new IllegalStateException(
- "Not running on Handler thread: " + Thread.currentThread().getName());
- }
- }
-
/*** Start monitoring sockets by listening callbacks for sockets creation or removal */
public void startMonitoringSockets() {
ensureRunningOnHandlerThread(mHandler);
@@ -258,7 +254,7 @@
Log.d(TAG, "Already monitoring sockets.");
return;
}
- if (DBG) Log.d(TAG, "Start monitoring sockets.");
+ mSharedLog.i("Start monitoring sockets.");
mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
new NetworkRequest.Builder().clearCapabilities().build(),
mNetworkCallback, mHandler);
@@ -287,6 +283,7 @@
// Only unregister the network callback if there is no socket request.
if (mCallbacksToRequestedNetworks.isEmpty()) {
+ mSharedLog.i("Stop monitoring sockets.");
mContext.getSystemService(ConnectivityManager.class)
.unregisterNetworkCallback(mNetworkCallback);
@@ -312,17 +309,10 @@
Log.d(TAG, "Monitoring sockets hasn't been started.");
return;
}
- if (DBG) Log.d(TAG, "Try to stop monitoring sockets.");
mRequestStop = true;
maybeStopMonitoringSockets();
}
- /*** Check whether the target network is matched current network */
- public static boolean isNetworkMatched(@Nullable Network targetNetwork,
- @Nullable Network currentNetwork) {
- return targetNetwork == null || targetNetwork.equals(currentNetwork);
- }
-
private boolean matchRequestedNetwork(Network network) {
return hasAllNetworksRequest()
|| mCallbacksToRequestedNetworks.containsValue(network);
@@ -424,17 +414,16 @@
if (networkKey == LOCAL_NET) {
transports = new int[0];
} else {
- transports = mActiveNetworksTransports.getOrDefault(
- ((NetworkAsKey) networkKey).mNetwork, new int[0]);
+ transports = mActiveNetworksTransports.get(((NetworkAsKey) networkKey).mNetwork);
+ if (transports == null) {
+ Log.wtf(TAG, "transports is missing for key: " + networkKey);
+ }
}
if (networkInterface == null || !isMdnsCapableInterface(networkInterface, transports)) {
return;
}
- if (DBG) {
- Log.d(TAG, "Create a socket on network:" + networkKey
- + " with interfaceName:" + interfaceName);
- }
+ mSharedLog.log("Create socket on net:" + networkKey + ", ifName:" + interfaceName);
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
@@ -455,7 +444,7 @@
notifySocketCreated(((NetworkAsKey) networkKey).mNetwork, socket, addresses);
}
} catch (IOException e) {
- Log.e(TAG, "Create a socket failed with interface=" + interfaceName, e);
+ mSharedLog.e("Create socket failed ifName:" + interfaceName, e);
}
}
@@ -484,7 +473,7 @@
// transports above in priority.
return iface.supportsMulticast();
} catch (SocketException e) {
- Log.e(TAG, "Error checking interface flags", e);
+ mSharedLog.e("Error checking interface flags", e);
return false;
}
}
@@ -495,6 +484,7 @@
socketInfo.mSocket.destroy();
notifyInterfaceDestroyed(network, socketInfo.mSocket);
+ mSharedLog.log("Remove socket on net:" + network);
}
private void removeTetherInterfaceSocket(String interfaceName) {
@@ -502,6 +492,7 @@
if (socketInfo == null) return;
socketInfo.mSocket.destroy();
notifyInterfaceDestroyed(null /* network */, socketInfo.mSocket);
+ mSharedLog.log("Remove socket on ifName:" + interfaceName);
}
private void notifySocketCreated(Network network, MdnsInterfaceSocket socket,
@@ -573,6 +564,7 @@
*/
public void requestSocket(@Nullable Network network, @NonNull SocketCallback cb) {
ensureRunningOnHandlerThread(mHandler);
+ mSharedLog.log("requestSocket for net:" + network);
mCallbacksToRequestedNetworks.put(cb, network);
if (network == null) {
// Does not specify a required network, create sockets for all possible
@@ -596,6 +588,7 @@
/*** Unrequest the socket */
public void unrequestSocket(@NonNull SocketCallback cb) {
ensureRunningOnHandlerThread(mHandler);
+ mSharedLog.log("unrequestSocket");
mCallbacksToRequestedNetworks.remove(cb);
if (hasAllNetworksRequest()) {
// Still has a request for all networks (interfaces).
@@ -608,8 +601,7 @@
if (matchRequestedNetwork(network)) continue;
final SocketInfo info = mNetworkSockets.removeAt(i);
info.mSocket.destroy();
- // Still notify to unrequester for socket destroy.
- cb.onInterfaceDestroyed(network, info.mSocket);
+ mSharedLog.log("Remove socket on net:" + network + " after unrequestSocket");
}
// Remove all sockets for tethering interface because these sockets do not have associated
@@ -618,8 +610,8 @@
for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
info.mSocket.destroy();
- // Still notify to unrequester for socket destroy.
- cb.onInterfaceDestroyed(null /* network */, info.mSocket);
+ mSharedLog.log("Remove socket on ifName:" + mTetherInterfaceSockets.keyAt(i)
+ + " after unrequestSocket");
}
mTetherInterfaceSockets.clear();
@@ -627,6 +619,7 @@
maybeStopMonitoringSockets();
}
+
/*** Callbacks for listening socket changes */
public interface SocketCallback {
/*** Notify the socket is created */
diff --git a/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
index 078c4dd..63119ac 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MulticastPacketReader.java
@@ -16,7 +16,7 @@
package com.android.server.connectivity.mdns;
-import static com.android.server.connectivity.mdns.MdnsSocketProvider.ensureRunningOnHandlerThread;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.annotation.NonNull;
import android.os.Handler;
@@ -63,8 +63,10 @@
*/
protected MulticastPacketReader(@NonNull String interfaceTag,
@NonNull ParcelFileDescriptor socket, @NonNull Handler handler,
- @NonNull byte[] buffer, int port) {
- super(handler, new RecvBuffer(buffer, new InetSocketAddress(port)));
+ @NonNull byte[] buffer) {
+ // Set the port to zero as placeholder as the recvfrom() call will fill the actual port
+ // value later.
+ super(handler, new RecvBuffer(buffer, new InetSocketAddress(0 /* port */)));
mLogTag = MulticastPacketReader.class.getSimpleName() + "/" + interfaceTag;
mSocket = socket;
mHandler = handler;
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
index 4650255..6bc7941 100644
--- a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
@@ -30,7 +30,7 @@
/**
* Creates a new netlink monitor.
*/
- public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
+ public static AbstractSocketNetlink createNetLinkMonitor(@NonNull final Handler handler,
@NonNull SharedLog log, @NonNull MdnsSocketProvider.NetLinkMonitorCallBack cb) {
return new SocketNetlinkMonitor(handler, log, cb);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index 6395b53..451909c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -28,13 +28,13 @@
import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.net.module.util.netlink.StructIfaddrMsg;
-import com.android.server.connectivity.mdns.ISocketNetLinkMonitor;
+import com.android.server.connectivity.mdns.AbstractSocketNetlink;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
/**
* The netlink monitor for MdnsSocketProvider.
*/
-public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
+public class SocketNetlinkMonitor extends NetlinkMonitor implements AbstractSocketNetlink {
public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
@@ -61,13 +61,11 @@
final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
msg.getFlags(), ifaddrMsg.scope);
- if (!la.isPreferred()) {
- // Skip the unusable ip address.
- return;
- }
switch (msg.getHeader().nlmsg_type) {
case NetlinkConstants.RTM_NEWADDR:
- mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+ if (la.isPreferred()) {
+ mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+ }
break;
case NetlinkConstants.RTM_DELADDR:
mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
new file mode 100644
index 0000000..bc94869
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Network;
+import android.os.Handler;
+
+import com.android.server.connectivity.mdns.MdnsConstants;
+import com.android.server.connectivity.mdns.MdnsRecord;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Mdns utility functions.
+ */
+public class MdnsUtils {
+
+ private MdnsUtils() { }
+
+ /**
+ * Convert the string to DNS case-insensitive lowercase
+ *
+ * Per rfc6762#page-46, accented characters are not defined to be automatically equivalent to
+ * their unaccented counterparts. So the "DNS lowercase" should be if character is A-Z then they
+ * transform into a-z. Otherwise, they are kept as-is.
+ */
+ public static String toDnsLowerCase(@NonNull String string) {
+ final char[] outChars = new char[string.length()];
+ for (int i = 0; i < string.length(); i++) {
+ outChars[i] = toDnsLowerCase(string.charAt(i));
+ }
+ return new String(outChars);
+ }
+
+ /**
+ * Convert the array of labels to DNS case-insensitive lowercase.
+ */
+ public static String[] toDnsLabelsLowerCase(@NonNull String[] labels) {
+ final String[] outStrings = new String[labels.length];
+ for (int i = 0; i < labels.length; ++i) {
+ outStrings[i] = toDnsLowerCase(labels[i]);
+ }
+ return outStrings;
+ }
+
+ /**
+ * Compare two strings by DNS case-insensitive lowercase.
+ */
+ public static boolean equalsIgnoreDnsCase(@NonNull String a, @NonNull String b) {
+ if (a.length() != b.length()) return false;
+ for (int i = 0; i < a.length(); i++) {
+ if (toDnsLowerCase(a.charAt(i)) != toDnsLowerCase(b.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare two set of DNS labels by DNS case-insensitive lowercase.
+ */
+ public static boolean equalsDnsLabelIgnoreDnsCase(@NonNull String[] a, @NonNull String[] b) {
+ if (a == b) {
+ return true;
+ }
+ int length = a.length;
+ if (b.length != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!equalsIgnoreDnsCase(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare labels a equals b or a is suffix of b.
+ *
+ * @param a the type or subtype.
+ * @param b the base type
+ */
+ public static boolean typeEqualsOrIsSubtype(@NonNull String[] a,
+ @NonNull String[] b) {
+ return MdnsUtils.equalsDnsLabelIgnoreDnsCase(a, b)
+ || ((b.length == (a.length + 2))
+ && MdnsUtils.equalsIgnoreDnsCase(b[1], MdnsConstants.SUBTYPE_LABEL)
+ && MdnsRecord.labelsAreSuffix(a, b));
+ }
+
+ private static char toDnsLowerCase(char a) {
+ return a >= 'A' && a <= 'Z' ? (char) (a + ('a' - 'A')) : a;
+ }
+
+ /*** Ensure that current running thread is same as given handler thread */
+ public static void ensureRunningOnHandlerThread(@NonNull Handler handler) {
+ if (!isRunningOnHandlerThread(handler)) {
+ throw new IllegalStateException(
+ "Not running on Handler thread: " + Thread.currentThread().getName());
+ }
+ }
+
+ /*** Check that current running thread is same as given handler thread */
+ public static boolean isRunningOnHandlerThread(@NonNull Handler handler) {
+ if (handler.getLooper().getThread() == Thread.currentThread()) {
+ return true;
+ }
+ return false;
+ }
+
+ /*** Check whether the target network is matched current network */
+ public static boolean isNetworkMatched(@Nullable Network targetNetwork,
+ @Nullable Network currentNetwork) {
+ return targetNetwork == null || targetNetwork.equals(currentNetwork);
+ }
+
+ /**
+ * Truncate a service name to up to maxLength UTF-8 bytes.
+ */
+ public static String truncateServiceName(@NonNull String originalName, int maxLength) {
+ // UTF-8 is at most 4 bytes per character; return early in the common case where
+ // the name can't possibly be over the limit given its string length.
+ if (originalName.length() <= maxLength / 4) return originalName;
+
+ final Charset utf8 = StandardCharsets.UTF_8;
+ final CharsetEncoder encoder = utf8.newEncoder();
+ final ByteBuffer out = ByteBuffer.allocate(maxLength);
+ // encode will write as many characters as possible to the out buffer, and just
+ // return an overflow code if there were too many characters (no need to check the
+ // return code here, this method truncates the name on purpose).
+ encoder.encode(CharBuffer.wrap(originalName), out, true /* endOfInput */);
+ return new String(out.array(), 0, out.position(), utf8);
+ }
+}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index eed9aeb..6776920 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.ConnectivityResources;
import android.net.EthernetManager;
import android.net.EthernetNetworkSpecifier;
import android.net.IpConfiguration;
@@ -50,6 +49,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.InterfaceParams;
+import com.android.server.connectivity.ConnectivityResources;
import java.io.FileDescriptor;
import java.util.Objects;
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index d520757..1f22b02 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.net.ConnectivityResources;
import android.net.EthernetManager;
import android.net.IEthernetServiceListener;
import android.net.INetd;
@@ -57,6 +56,7 @@
import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.RtNetlinkLinkMessage;
import com.android.net.module.util.netlink.StructIfinfoMsg;
+import com.android.server.connectivity.ConnectivityResources;
import java.io.FileDescriptor;
import java.net.InetAddress;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 961337d..e7ef510 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -46,6 +46,7 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_TEST;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -83,7 +84,6 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
-import android.net.ConnectivityResources;
import android.net.DataUsageRequest;
import android.net.INetd;
import android.net.INetworkStatsService;
@@ -175,6 +175,7 @@
import com.android.networkstack.apishim.ConstantsShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.BpfNetMaps;
+import com.android.server.connectivity.ConnectivityResources;
import java.io.File;
import java.io.FileDescriptor;
@@ -946,7 +947,11 @@
@GuardedBy("mStatsLock")
private void shutdownLocked() {
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
- tetheringManager.unregisterTetheringEventCallback(mTetherListener);
+ try {
+ tetheringManager.unregisterTetheringEventCallback(mTetherListener);
+ } catch (IllegalStateException e) {
+ Log.i(TAG, "shutdownLocked: error when unregister tethering, ignored. e=" + e);
+ }
mContext.unregisterReceiver(mPollReceiver);
mContext.unregisterReceiver(mRemovedReceiver);
mContext.unregisterReceiver(mUserReceiver);
@@ -1578,7 +1583,9 @@
// For a template with wifi network keys, it is possible for a malicious
// client to track the user locations via querying data usage. Thus, enforce
// fine location permission check.
- if (!template.getWifiNetworkKeys().isEmpty()) {
+ // For a template with MATCH_TEST, since the wifi network key is just a placeholder
+ // to identify a specific test network, it is not related to track user location.
+ if (!template.getWifiNetworkKeys().isEmpty() && template.getMatchRule() != MATCH_TEST) {
final boolean canAccessFineLocation = mLocationPermissionChecker
.checkCallersLocationPermission(callingPackage,
null /* featureId */,
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 77cffda..9ced44e 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -54,6 +54,10 @@
if (!isOk(status)) {
uid_t uid = getuid();
ALOGE("BpfNetMaps jni init failure as uid=%d", uid);
+ // We probably only ever get called from system_server (ie. AID_SYSTEM)
+ // or from tests, and never from network_stack (ie. AID_NETWORK_STACK).
+ // However, if we ever do add calls from production network_stack code
+ // we do want to make sure this initializes correctly.
// TODO: Fix tests to not use this jni lib, so we can unconditionally abort()
if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort();
}
diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp
index 7aeecfa..08d31a3 100644
--- a/service/jni/com_android_server_TestNetworkService.cpp
+++ b/service/jni/com_android_server_TestNetworkService.cpp
@@ -38,9 +38,14 @@
#include "jni.h"
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <bpf/KernelUtils.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
+#ifndef IFF_NO_CARRIER
+#define IFF_NO_CARRIER 0x0040
+#endif
+
namespace android {
//------------------------------------------------------------------------------
@@ -66,17 +71,21 @@
// Allocate interface.
ifr.ifr_flags = (isTun ? IFF_TUN : IFF_TAP) | IFF_NO_PI;
+ if (!hasCarrier) {
+ // Using IFF_NO_CARRIER is supported starting in kernel version >= 6.0
+ // Up until then, unsupported flags are ignored.
+ if (!bpf::isAtLeastKernelVersion(6, 0, 0)) {
+ throwException(env, EOPNOTSUPP, "IFF_NO_CARRIER not supported", ifr.ifr_name);
+ return -1;
+ }
+ ifr.ifr_flags |= IFF_NO_CARRIER;
+ }
strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
if (ioctl(tun.get(), TUNSETIFF, &ifr)) {
throwException(env, errno, "allocating", ifr.ifr_name);
return -1;
}
- if (!hasCarrier) {
- // disable carrier before setting IFF_UP
- setTunTapCarrierEnabledImpl(env, iface, tun.get(), hasCarrier);
- }
-
// Mark some TAP interfaces as supporting multicast
if (setIffMulticast && !isTun) {
base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index e63e423..c125bd6 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -51,7 +51,9 @@
namespace android {
-#define ALOGF(s ...) do { ALOGE(s); abort(); } while(0)
+static bool fatal = false;
+
+#define ALOGF(s ...) do { ALOGE(s); fatal = true; } while(0)
enum verify { VERIFY_DIR, VERIFY_BIN, VERIFY_PROG, VERIFY_MAP_RO, VERIFY_MAP_RW };
@@ -88,11 +90,6 @@
#undef ALOGF
-bool isGsiImage() {
- // this implementation matches 2 other places in the codebase (same function name too)
- return !access("/system/system_ext/etc/init/init.gsi.rc", F_OK);
-}
-
static const char* kClatdDir = "/apex/com.android.tethering/bin/for-system";
static const char* kClatdBin = "/apex/com.android.tethering/bin/for-system/clatd";
@@ -115,11 +112,6 @@
// Clat BPF was only mainlined during T.
if (!modules::sdklevel::IsAtLeastT()) return;
- // HACK: some old vendor kernels lack ~5.10 backport of 'bpffs selinux genfscon' support.
- // This is *NOT* supported, but let's allow, at least for now, U+ GSI to boot on them.
- // (without this hack pixel5 R vendor + U gsi breaks)
- if (isGsiImage() && !bpf::isAtLeastKernelVersion(5, 10, 0)) return;
-
V("/sys/fs/bpf", S_IFDIR|S_ISVTX|0777, ROOT, ROOT, "fs_bpf", DIR);
V("/sys/fs/bpf/net_shared", S_IFDIR|S_ISVTX|0777, ROOT, ROOT, "fs_bpf_net_shared", DIR);
@@ -138,6 +130,7 @@
#undef V2
+ if (fatal) abort();
}
#undef V
@@ -479,11 +472,15 @@
static constexpr int WAITPID_ATTEMPTS = 50;
static constexpr int WAITPID_RETRY_INTERVAL_US = 100000;
-static void stopClatdProcess(int pid) {
- int err = kill(pid, SIGTERM);
- if (err) {
- err = errno;
+static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
+ jint pid) {
+ if (pid <= 0) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid pid");
+ return;
}
+
+ int err = kill(pid, SIGTERM);
+ if (err) err = errno;
if (err == ESRCH) {
ALOGE("clatd child process %d unexpectedly disappeared", pid);
return;
@@ -512,23 +509,6 @@
}
}
-static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
- jstring iface, jstring pfx96,
- jstring v4, jstring v6,
- jint pid) {
- ScopedUtfChars ifaceStr(env, iface);
- ScopedUtfChars pfx96Str(env, pfx96);
- ScopedUtfChars v4Str(env, v4);
- ScopedUtfChars v6Str(env, v6);
-
- if (pid <= 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
- return;
- }
-
- stopClatdProcess(pid);
-}
-
static jlong com_android_server_connectivity_ClatCoordinator_getSocketCookie(
JNIEnv* env, jclass clazz, jobject sockJavaFd) {
int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
@@ -538,7 +518,7 @@
}
uint64_t sock_cookie = bpf::getSocketCookie(sockFd);
- if (sock_cookie == bpf::NONEXISTENT_COOKIE) {
+ if (!sock_cookie) {
throwIOException(env, "get socket cookie failed", errno);
return -1;
}
@@ -573,8 +553,7 @@
"(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)com_android_server_connectivity_ClatCoordinator_startClatd},
- {"native_stopClatd",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
+ {"native_stopClatd", "(I)V",
(void*)com_android_server_connectivity_ClatCoordinator_stopClatd},
{"native_getSocketCookie", "(Ljava/io/FileDescriptor;)J",
(void*)com_android_server_connectivity_ClatCoordinator_getSocketCookie},
diff --git a/service/jni/onload.cpp b/service/jni/onload.cpp
index 3d15d43..ed74430 100644
--- a/service/jni/onload.cpp
+++ b/service/jni/onload.cpp
@@ -22,8 +22,8 @@
namespace android {
int register_com_android_server_TestNetworkService(JNIEnv* env);
-int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
int register_com_android_server_BpfNetMaps(JNIEnv* env);
+int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
@@ -38,15 +38,15 @@
return JNI_ERR;
}
- if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
- return JNI_ERR;
- }
-
- if (register_com_android_server_BpfNetMaps(env) < 0) {
- return JNI_ERR;
- }
-
if (android::modules::sdklevel::IsAtLeastT()) {
+ if (register_com_android_server_BpfNetMaps(env) < 0) {
+ return JNI_ERR;
+ }
+
+ if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
+ return JNI_ERR;
+ }
+
if (register_android_server_net_NetworkStatsFactory(env) < 0) {
return JNI_ERR;
}
diff --git a/service/libconnectivity/Android.bp b/service/libconnectivity/Android.bp
index 391ceac..e063af7 100644
--- a/service/libconnectivity/Android.bp
+++ b/service/libconnectivity/Android.bp
@@ -27,6 +27,7 @@
min_sdk_version: "30",
static_libs: [
"connectivity_native_aidl_interface-V1-ndk",
+ "libmodules-utils-build",
],
export_include_dirs: ["include"],
cflags: [
diff --git a/service/libconnectivity/src/connectivity_native.cpp b/service/libconnectivity/src/connectivity_native.cpp
index a476498..f39bb0a 100644
--- a/service/libconnectivity/src/connectivity_native.cpp
+++ b/service/libconnectivity/src/connectivity_native.cpp
@@ -17,6 +17,7 @@
#include "connectivity_native.h"
#include <android/binder_manager.h>
+#include <android-modules-utils/sdk_level.h>
#include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
using aidl::android::net::connectivity::aidl::IConnectivityNative;
@@ -44,6 +45,7 @@
}
int AConnectivityNative_blockPortForBind(in_port_t port) {
+ if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
std::shared_ptr<IConnectivityNative> c = getBinder();
if (!c) {
return EAGAIN;
@@ -52,6 +54,7 @@
}
int AConnectivityNative_unblockPortForBind(in_port_t port) {
+ if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
std::shared_ptr<IConnectivityNative> c = getBinder();
if (!c) {
return EAGAIN;
@@ -60,6 +63,7 @@
}
int AConnectivityNative_unblockAllPortsForBind() {
+ if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
std::shared_ptr<IConnectivityNative> c = getBinder();
if (!c) {
return EAGAIN;
@@ -68,6 +72,7 @@
}
int AConnectivityNative_getPortsBlockedForBind(in_port_t *ports, size_t *count) {
+ if (!android::modules::sdklevel::IsAtLeastU()) return ENOSYS;
std::shared_ptr<IConnectivityNative> c = getBinder();
if (!c) {
return EAGAIN;
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index b4fce37..ec168dd 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -281,8 +281,8 @@
if (sEnableJavaBpfMap == null) {
sEnableJavaBpfMap = SdkLevel.isAtLeastU() ||
DeviceConfigUtils.isFeatureEnabled(context,
- DeviceConfig.NAMESPACE_TETHERING, BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP,
- false /* defaultValue */);
+ DeviceConfig.NAMESPACE_TETHERING, BPF_NET_MAPS_ENABLE_JAVA_BPF_MAP,
+ DeviceConfigUtils.TETHERING_MODULE_NAME, false /* defaultValue */);
}
Log.d(TAG, "BpfNetMaps is initialized with sEnableJavaBpfMap=" + sEnableJavaBpfMap);
@@ -384,7 +384,6 @@
* ALLOWLIST means the firewall denies all by default, uids must be explicitly allowed
* DENYLIST means the firewall allows all by default, uids must be explicitly denyed
*/
- @VisibleForTesting
public boolean isFirewallAllowList(final int chain) {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
@@ -745,6 +744,65 @@
}
}
+ private Set<Integer> getUidsMatchEnabled(final int childChain) throws ErrnoException {
+ final long match = getMatchByFirewallChain(childChain);
+ Set<Integer> uids = new ArraySet<>();
+ synchronized (sUidOwnerMap) {
+ sUidOwnerMap.forEach((uid, val) -> {
+ if (val == null) {
+ Log.wtf(TAG, "sUidOwnerMap entry was deleted while holding a lock");
+ } else {
+ if ((val.rule & match) != 0) {
+ uids.add(uid.val);
+ }
+ }
+ });
+ }
+ return uids;
+ }
+
+ /**
+ * Get uids that has FIREWALL_RULE_ALLOW on allowlist chain.
+ * Allowlist means the firewall denies all by default, uids must be explicitly allowed.
+ *
+ * Note that uids that has FIREWALL_RULE_DENY on allowlist chain can not be computed from the
+ * bpf map, since all the uids that does not have explicit FIREWALL_RULE_ALLOW rule in bpf map
+ * are determined to have FIREWALL_RULE_DENY.
+ *
+ * @param childChain target chain
+ * @return Set of uids
+ */
+ public Set<Integer> getUidsWithAllowRuleOnAllowListChain(final int childChain)
+ throws ErrnoException {
+ if (!isFirewallAllowList(childChain)) {
+ throw new IllegalArgumentException("getUidsWithAllowRuleOnAllowListChain is called with"
+ + " denylist chain:" + childChain);
+ }
+ // Corresponding match is enabled for uids that has FIREWALL_RULE_ALLOW on allowlist chain.
+ return getUidsMatchEnabled(childChain);
+ }
+
+ /**
+ * Get uids that has FIREWALL_RULE_DENY on denylist chain.
+ * Denylist means the firewall allows all by default, uids must be explicitly denyed
+ *
+ * Note that uids that has FIREWALL_RULE_ALLOW on denylist chain can not be computed from the
+ * bpf map, since all the uids that does not have explicit FIREWALL_RULE_DENY rule in bpf map
+ * are determined to have the FIREWALL_RULE_ALLOW.
+ *
+ * @param childChain target chain
+ * @return Set of uids
+ */
+ public Set<Integer> getUidsWithDenyRuleOnDenyListChain(final int childChain)
+ throws ErrnoException {
+ if (isFirewallAllowList(childChain)) {
+ throw new IllegalArgumentException("getUidsWithDenyRuleOnDenyListChain is called with"
+ + " allowlist chain:" + childChain);
+ }
+ // Corresponding match is enabled for uids that has FIREWALL_RULE_DENY on denylist chain.
+ return getUidsMatchEnabled(childChain);
+ }
+
/**
* Add ingress interface filtering rules to a list of UIDs
*
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ba503e0..98ad861 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.FEATURE_WIFI;
@@ -91,7 +92,7 @@
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
-import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.ETH_P_ALL;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -108,8 +109,9 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.app.ActivityManager.UidFrozenStateChangedCallback;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -134,7 +136,6 @@
import android.net.ConnectivityManager.BlockedReason;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.RestrictBackgroundStatus;
-import android.net.ConnectivityResources;
import android.net.ConnectivitySettingsManager;
import android.net.DataStallReportParcelable;
import android.net.DnsResolverServiceManager;
@@ -244,6 +245,7 @@
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -280,11 +282,13 @@
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
+import com.android.server.connectivity.ConnectivityResources;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.DscpPolicyTracker;
import com.android.server.connectivity.FullScore;
import com.android.server.connectivity.InvalidTagException;
+import com.android.server.connectivity.KeepaliveResourceUtil;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.MockableSystemProperties;
@@ -310,11 +314,13 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -783,6 +789,11 @@
private static final int EVENT_SET_LOW_TCP_POLLING_UNTIL = 60;
/**
+ * Event to inform the ConnectivityService handler when a uid has been frozen or unfrozen.
+ */
+ private static final int EVENT_UID_FROZEN_STATE_CHANGED = 61;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -1267,6 +1278,18 @@
return Binder.getCallingUid();
}
+ public boolean isAtLeastS() {
+ return SdkLevel.isAtLeastS();
+ }
+
+ public boolean isAtLeastT() {
+ return SdkLevel.isAtLeastT();
+ }
+
+ public boolean isAtLeastU() {
+ return SdkLevel.isAtLeastU();
+ }
+
/**
* Get system properties to use in ConnectivityService.
*/
@@ -1379,7 +1402,7 @@
@Nullable
public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
@NonNull final Context context, @NonNull final TelephonyManager tm) {
- if (SdkLevel.isAtLeastT()) {
+ if (isAtLeastT()) {
return new CarrierPrivilegeAuthenticator(context, tm);
} else {
return null;
@@ -1389,9 +1412,9 @@
/**
* @see DeviceConfigUtils#isFeatureEnabled
*/
- public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
- return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
- TETHERING_MODULE_NAME, defaultEnabled);
+ public boolean isFeatureEnabled(Context context, String name) {
+ return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_TETHERING, name,
+ TETHERING_MODULE_NAME, false /* defaultValue */);
}
/**
@@ -1485,6 +1508,53 @@
@NonNull final UserHandle user) {
return CompatChanges.isChangeEnabled(changeId, packageName, user);
}
+
+ /**
+ * As above but with a UID.
+ * @see CompatChanges#isChangeEnabled(long, int)
+ */
+ public boolean isChangeEnabled(final long changeId, final int uid) {
+ return CompatChanges.isChangeEnabled(changeId, uid);
+ }
+
+ /**
+ * Call {@link InetDiagMessage#destroyLiveTcpSockets(Set, Set)}
+ *
+ * @param ranges target uid ranges
+ * @param exemptUids uids to skip close socket
+ */
+ public void destroyLiveTcpSockets(@NonNull final Set<Range<Integer>> ranges,
+ @NonNull final Set<Integer> exemptUids)
+ throws SocketException, InterruptedIOException, ErrnoException {
+ InetDiagMessage.destroyLiveTcpSockets(ranges, exemptUids);
+ }
+
+ /**
+ * Call {@link InetDiagMessage#destroyLiveTcpSocketsByOwnerUids(Set)}
+ *
+ * @param ownerUids target uids to close sockets
+ */
+ public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids)
+ throws SocketException, InterruptedIOException, ErrnoException {
+ InetDiagMessage.destroyLiveTcpSocketsByOwnerUids(ownerUids);
+ }
+
+ /**
+ * Schedule the evaluation timeout.
+ *
+ * When a network connects, it's "not evaluated" yet. Detection events cause the network
+ * to be "evaluated" (typically, validation or detection of a captive portal). If none
+ * of these events happen, this time will run out, after which the network is considered
+ * "evaluated" even if nothing happened to it. Notionally that means the system gave up
+ * on this network and considers it won't provide connectivity. In particular, that means
+ * it's when the system prefers it to cell if it's wifi and configuration says it should
+ * prefer bad wifi to cell.
+ */
+ public void scheduleEvaluationTimeout(@NonNull Handler handler,
+ @NonNull final Network network, final long delayMs) {
+ handler.sendMessageDelayed(
+ handler.obtainMessage(EVENT_INITIAL_EVALUATION_TIMEOUT, network), delayMs);
+ }
}
public ConnectivityService(Context context) {
@@ -1660,7 +1730,7 @@
// Even if it could, running on S would at least require mocking out the BPF map,
// otherwise the unit tests will fail on pre-T devices where the seccomp filter blocks
// the bpf syscall. http://aosp/1907693
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
mDscpPolicyTracker = new DscpPolicyTracker();
}
} catch (ErrnoException e) {
@@ -1670,11 +1740,37 @@
mIngressRateLimit = ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
mContext);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
mCdmps = new CompanionDeviceManagerProxyService(context);
} else {
mCdmps = null;
}
+
+ if (mDeps.isAtLeastU()
+ && mDeps.isFeatureEnabled(context, KEY_DESTROY_FROZEN_SOCKETS_VERSION)) {
+ final UidFrozenStateChangedCallback frozenStateChangedCallback =
+ new UidFrozenStateChangedCallback() {
+ @Override
+ public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
+ if (uids.length != frozenStates.length) {
+ Log.wtf(TAG, "uids has length " + uids.length
+ + " but frozenStates has length " + frozenStates.length);
+ return;
+ }
+
+ final UidFrozenStateChangedArgs args =
+ new UidFrozenStateChangedArgs(uids, frozenStates);
+
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_UID_FROZEN_STATE_CHANGED, args));
+ }
+ };
+
+ final ActivityManager activityManager =
+ mContext.getSystemService(ActivityManager.class);
+ activityManager.registerUidFrozenStateChangedCallback(
+ (Runnable r) -> r.run(), frozenStateChangedCallback);
+ }
}
/**
@@ -2603,7 +2699,8 @@
final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null && nai.everConnected()) {
+ final boolean includeNetwork = (nai != null) && nai.isCreated();
+ if (includeNetwork) {
// TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -2843,6 +2940,39 @@
setUidBlockedReasons(uid, blockedReasons);
}
+ static final class UidFrozenStateChangedArgs {
+ final int[] mUids;
+ final int[] mFrozenStates;
+
+ UidFrozenStateChangedArgs(int[] uids, int[] frozenStates) {
+ mUids = uids;
+ mFrozenStates = frozenStates;
+ }
+ }
+
+ private void handleFrozenUids(int[] uids, int[] frozenStates) {
+ final ArraySet<Range<Integer>> ranges = new ArraySet<>();
+
+ for (int i = 0; i < uids.length; i++) {
+ if (frozenStates[i] == UID_FROZEN_STATE_FROZEN) {
+ Integer uidAsInteger = Integer.valueOf(uids[i]);
+ ranges.add(new Range(uidAsInteger, uidAsInteger));
+ }
+ }
+
+ if (!ranges.isEmpty()) {
+ final Set<Integer> exemptUids = new ArraySet<>();
+ try {
+ mDeps.destroyLiveTcpSockets(ranges, exemptUids);
+ } catch (Exception e) {
+ loge("Exception in socket destroy: " + e);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static final String KEY_DESTROY_FROZEN_SOCKETS_VERSION = "destroy_frozen_sockets_version";
+
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -3084,8 +3214,6 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
// TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
@TargetApi(Build.VERSION_CODES.S)
private void sendStickyBroadcast(Intent intent) {
@@ -3121,7 +3249,7 @@
private void applyMostRecentPolicyForConnectivityAction(BroadcastOptions options,
NetworkInfo info) {
// Delivery group policy APIs are only available on U+.
- if (!SdkLevel.isAtLeastU()) return;
+ if (!mDeps.isAtLeastU()) return;
final BroadcastOptionsShim optsShim = mDeps.makeBroadcastOptionsShim(options);
try {
@@ -3199,7 +3327,7 @@
}
// On T+ devices, register callback for statsd to pull NETWORK_BPF_MAP_INFO atom
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
mBpfNetMaps.setPullAtomCallback(mContext);
}
// Wait PermissionMonitor to finish the permission update. Then MultipathPolicyTracker won't
@@ -3796,9 +3924,9 @@
break;
}
case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: {
- if (!nai.isCreated()) {
- Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString()
- + ", tearing down instead");
+ if (!nai.everConnected()) {
+ Log.d(TAG, "unregisterAfterReplacement on never-connected "
+ + nai.toShortString() + ", tearing down instead");
teardownUnneededNetwork(nai);
break;
}
@@ -4383,6 +4511,25 @@
}
}
+ @VisibleForTesting
+ protected static boolean shouldCreateNetworksImmediately() {
+ // Before U, physical networks are only created when the agent advances to CONNECTED.
+ // In U and above, all networks are immediately created when the agent is registered.
+ return SdkLevel.isAtLeastU();
+ }
+
+ private static boolean shouldCreateNativeNetwork(@NonNull NetworkAgentInfo nai,
+ @NonNull NetworkInfo.State state) {
+ if (nai.isCreated()) return false;
+ if (state == NetworkInfo.State.CONNECTED) return true;
+ if (state != NetworkInfo.State.CONNECTING) {
+ // TODO: throw if no WTFs are observed in the field.
+ Log.wtf(TAG, "Uncreated network in invalid state: " + state);
+ return false;
+ }
+ return nai.isVPN() || shouldCreateNetworksImmediately();
+ }
+
private static boolean shouldDestroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
return nai.isCreated() && !nai.isDestroyed();
}
@@ -4390,7 +4537,7 @@
@VisibleForTesting
boolean shouldIgnoreValidationFailureAfterRoam(NetworkAgentInfo nai) {
// T+ devices should use unregisterAfterReplacement.
- if (SdkLevel.isAtLeastT()) return false;
+ if (mDeps.isAtLeastT()) return false;
// If the network never roamed, return false. The check below is not sufficient if time
// since boot is less than blockTimeOut, though that's extremely unlikely to happen.
@@ -4433,7 +4580,7 @@
// because they lost all their requests or because their score isn't good)
// then they would disconnect organically, report their new state and then
// disconnect the channel.
- if (nai.networkInfo.isConnected()) {
+ if (nai.networkInfo.isConnected() || nai.networkInfo.isSuspended()) {
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
@@ -4529,7 +4676,7 @@
// for an unnecessarily long time.
destroyNativeNetwork(nai);
}
- if (!nai.isCreated() && !SdkLevel.isAtLeastT()) {
+ if (!nai.isCreated() && !mDeps.isAtLeastT()) {
// Backwards compatibility: send onNetworkDestroyed even if network was never created.
// This can never run if the code above runs because shouldDestroyNativeNetwork is
// false if the network was never created.
@@ -4613,7 +4760,7 @@
}
private void checkNrisConsistency(final NetworkRequestInfo nri) {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
for (final NetworkRequestInfo n : mNetworkRequests.values()) {
if (n.mBinder != null && n.mBinder == nri.mBinder) {
// Temporary help to debug b/194394697 ; TODO : remove this function when the
@@ -5160,8 +5307,7 @@
/** Schedule evaluation timeout */
@VisibleForTesting
public void scheduleEvaluationTimeout(@NonNull final Network network, final long delayMs) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(EVENT_INITIAL_EVALUATION_TIMEOUT, network), delayMs);
+ mDeps.scheduleEvaluationTimeout(mHandler, network, delayMs);
}
@Override
@@ -5706,6 +5852,10 @@
mKeepaliveTracker.handleSetTestLowTcpPollingTimer(time);
break;
}
+ case EVENT_UID_FROZEN_STATE_CHANGED:
+ UidFrozenStateChangedArgs args = (UidFrozenStateChangedArgs) msg.obj;
+ handleFrozenUids(args.mUids, args.mFrozenStates);
+ break;
}
}
}
@@ -6200,7 +6350,7 @@
+ Arrays.toString(ranges) + "): netd command failed: " + e);
}
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
mPermissionMonitor.updateVpnLockdownUidRanges(requireVpn, ranges);
}
@@ -6994,7 +7144,7 @@
@NonNull final NetworkCapabilities networkCapabilities) {
// This check is added to fix the linter error for "current min is 30", which is not going
// to happen because Connectivity service always run in S+.
- if (!SdkLevel.isAtLeastS()) {
+ if (!mDeps.isAtLeastS()) {
Log.wtf(TAG, "Connectivity service should always run in at least SDK S");
return;
}
@@ -7821,7 +7971,7 @@
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
- } else {
+ } else if (networkAgent.everConnected()) {
updateProxy(newLp, oldLp);
}
@@ -7855,6 +8005,10 @@
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
+ private void applyInitialLinkProperties(@NonNull NetworkAgentInfo nai) {
+ updateLinkProperties(nai, new LinkProperties(nai.linkProperties), null);
+ }
+
/**
* @param naData captive portal data from NetworkAgent
* @param apiData captive portal data from capport API
@@ -7904,16 +8058,27 @@
return captivePortalBuilder.build();
}
- private String makeNflogPrefix(String iface, long networkHandle) {
+ @VisibleForTesting
+ static String makeNflogPrefix(String iface, long networkHandle) {
// This needs to be kept in sync and backwards compatible with the decoding logic in
// NetdEventListenerService, which is non-mainline code.
return SdkLevel.isAtLeastU() ? (networkHandle + ":" + iface) : ("iface:" + iface);
}
+ private static boolean isWakeupMarkingSupported(NetworkCapabilities capabilities) {
+ if (capabilities.hasTransport(TRANSPORT_WIFI)) {
+ return true;
+ }
+ if (SdkLevel.isAtLeastU() && capabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ return true;
+ }
+ return false;
+ }
+
private void wakeupModifyInterface(String iface, NetworkAgentInfo nai, boolean add) {
// Marks are only available on WiFi interfaces. Checking for
// marks on unsupported interfaces is harmless.
- if (!nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ if (!isWakeupMarkingSupported(nai.networkCapabilities)) {
return;
}
@@ -8416,7 +8581,7 @@
// On T and above, allow rules are needed for all VPNs. Allow rule with null iface is a
// wildcard to allow apps to receive packets on all interfaces. This is required to accept
// incoming traffic in Lockdown mode by overriding the Lockdown blocking rule.
- return SdkLevel.isAtLeastT() && nai.isVPN() && lp != null && lp.getInterfaceName() != null;
+ return mDeps.isAtLeastT() && nai.isVPN() && lp != null && lp.getInterfaceName() != null;
}
private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
@@ -8448,11 +8613,11 @@
return stableRanges;
}
- private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges,
- int[] exemptUids) {
+ private void maybeCloseSockets(NetworkAgentInfo nai, Set<UidRange> ranges,
+ Set<Integer> exemptUids) {
if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
try {
- mNetd.socketDestroy(ranges, exemptUids);
+ mDeps.destroyLiveTcpSockets(UidRange.toIntRanges(ranges), exemptUids);
} catch (Exception e) {
loge("Exception in socket destroy: ", e);
}
@@ -8460,16 +8625,16 @@
}
private void updateVpnUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
- int[] exemptUids = new int[2];
+ final Set<Integer> exemptUids = new ArraySet<>();
// TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
// by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
// starting a legacy VPN, and remove VPN_UID here. (b/176542831)
- exemptUids[0] = VPN_UID;
- exemptUids[1] = nai.networkCapabilities.getOwnerUid();
+ exemptUids.add(VPN_UID);
+ exemptUids.add(nai.networkCapabilities.getOwnerUid());
UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
// Close sockets before modifying uid ranges so that RST packets can reach to the server.
- maybeCloseSockets(nai, ranges, exemptUids);
+ maybeCloseSockets(nai, uidRanges, exemptUids);
try {
if (add) {
mNetd.networkAddUidRangesParcel(new NativeUidRangeConfig(
@@ -8483,7 +8648,7 @@
" on netId " + nai.network.netId + ". " + e);
}
// Close sockets that established connection while requesting netd.
- maybeCloseSockets(nai, ranges, exemptUids);
+ maybeCloseSockets(nai, uidRanges, exemptUids);
}
private boolean isProxySetOnAnyDefaultNetwork() {
@@ -8659,21 +8824,19 @@
// else not handled
}
- // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
- @SuppressLint("NewApi")
private void sendIntent(PendingIntent pendingIntent, Intent intent) {
mPendingIntentWakeLock.acquire();
try {
if (DBG) log("Sending " + pendingIntent);
final BroadcastOptions options = BroadcastOptions.makeBasic();
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
// Explicitly disallow the receiver from starting activities, to prevent apps from
// utilizing the PendingIntent as a backdoor to do this.
options.setPendingIntentBackgroundActivityLaunchAllowed(false);
}
pendingIntent.send(mContext, 0, intent, this /* onFinished */, null /* Handler */,
null /* requiredPermission */,
- SdkLevel.isAtLeastT() ? options.toBundle() : null);
+ mDeps.isAtLeastT() ? options.toBundle() : null);
} catch (PendingIntent.CanceledException e) {
if (DBG) log(pendingIntent + " was not sent, it had been canceled.");
mPendingIntentWakeLock.release();
@@ -8952,7 +9115,7 @@
private void updateProfileAllowedNetworks() {
// Netd command is not implemented before U.
- if (!SdkLevel.isAtLeastU()) return;
+ if (!mDeps.isAtLeastU()) return;
ensureRunningOnConnectivityServiceThread();
final ArrayList<NativeUidRangeConfig> configs = new ArrayList<>();
@@ -9606,21 +9769,32 @@
+ oldInfo.getState() + " to " + state);
}
- if (!networkAgent.isCreated()
- && (state == NetworkInfo.State.CONNECTED
- || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
-
+ if (shouldCreateNativeNetwork(networkAgent, state)) {
// A network that has just connected has zero requests and is thus a foreground network.
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
+
+ networkAgent.setCreated();
+
+ // If the network is created immediately on register, then apply the LinkProperties now.
+ // Otherwise, this is done further down when the network goes into connected state.
+ // Applying the LinkProperties means that the network is ready to carry traffic -
+ // interfaces and routing rules have been added, DNS servers programmed, etc.
+ // For VPNs, this must be done before the capabilities are updated, because as soon as
+ // that happens, UIDs are routed to the network.
+ if (shouldCreateNetworksImmediately()) {
+ applyInitialLinkProperties(networkAgent);
+ }
+
+ // TODO: should this move earlier? It doesn't seem to have anything to do with whether
+ // a network is created or not.
if (networkAgent.propagateUnderlyingCapabilities()) {
// Initialize the network's capabilities to their starting values according to the
// underlying networks. This ensures that the capabilities are correct before
// anything happens to the network.
updateCapabilitiesForNetwork(networkAgent);
}
- networkAgent.setCreated();
networkAgent.onNetworkCreated();
updateAllowedUids(networkAgent, null, networkAgent.networkCapabilities);
updateProfileAllowedNetworks();
@@ -9634,8 +9808,19 @@
networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
- updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
- null);
+ if (!shouldCreateNetworksImmediately()) {
+ applyInitialLinkProperties(networkAgent);
+ } else {
+ // The network was created when the agent registered, and the LinkProperties are
+ // already up-to-date. However, updateLinkProperties also makes some changes only
+ // when the network connects. Apply those changes here. On T and below these are
+ // handled by the applyInitialLinkProperties call just above.
+ // TODO: stop relying on updateLinkProperties(..., null) to do this.
+ // If something depends on both LinkProperties and connected state, it should be in
+ // this method as well.
+ networkAgent.clatd.update();
+ updateProxy(networkAgent.linkProperties, null);
+ }
// If a rate limit has been configured and is applicable to this network (network
// provides internet connectivity), apply it. The tc police filter cannot be attached
@@ -9666,13 +9851,13 @@
// in the absence of a satisfactory, scalable solution which follows an easy/standard
// process to check the interface version, just use an SDK check. NetworkStack will
// always be new enough when running on T+.
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
networkAgent.networkMonitor().notifyNetworkConnected(params);
} else {
networkAgent.networkMonitor().notifyNetworkConnected(params.linkProperties,
params.networkCapabilities);
}
- final long delay = activelyPreferBadWifi()
+ final long delay = !avoidBadWifi() && activelyPreferBadWifi()
? ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS
: DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS;
scheduleEvaluationTimeout(networkAgent.network, delay);
@@ -10021,6 +10206,16 @@
}
@Override
+ public int[] getSupportedKeepalives() {
+ enforceAnyPermissionOf(mContext, android.Manifest.permission.NETWORK_SETTINGS,
+ // Backwards compatibility with CTS 13
+ android.Manifest.permission.QUERY_ALL_PACKAGES);
+
+ return BinderUtils.withCleanCallingIdentity(() ->
+ KeepaliveResourceUtil.getSupportedKeepalives(mContext));
+ }
+
+ @Override
public void factoryReset() {
enforceSettingsPermission();
@@ -11132,7 +11327,7 @@
if (um.isManagedProfile(profile.getIdentifier())) {
return true;
}
- if (SdkLevel.isAtLeastT() && dpm.getDeviceOwner() != null) return true;
+ if (mDeps.isAtLeastT() && dpm.getDeviceOwner() != null) return true;
return false;
}
@@ -11414,7 +11609,7 @@
private boolean canNetworkBeRateLimited(@NonNull final NetworkAgentInfo networkAgent) {
// Rate-limiting cannot run correctly before T because the BPF program is not loaded.
- if (!SdkLevel.isAtLeastT()) return false;
+ if (!mDeps.isAtLeastT()) return false;
final NetworkCapabilities agentCaps = networkAgent.networkCapabilities;
// Only test networks (they cannot hold NET_CAPABILITY_INTERNET) and networks that provide
@@ -11894,6 +12089,23 @@
return rule;
}
+ private void closeSocketsForFirewallChainLocked(final int chain)
+ throws ErrnoException, SocketException, InterruptedIOException {
+ if (mBpfNetMaps.isFirewallAllowList(chain)) {
+ // Allowlist means the firewall denies all by default, uids must be explicitly allowed
+ // So, close all non-system socket owned by uids that are not explicitly allowed
+ Set<Range<Integer>> ranges = new ArraySet<>();
+ ranges.add(new Range<>(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE));
+ final Set<Integer> exemptUids = mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(chain);
+ mDeps.destroyLiveTcpSockets(ranges, exemptUids);
+ } else {
+ // Denylist means the firewall allows all by default, uids must be explicitly denied
+ // So, close socket owned by uids that are explicitly denied
+ final Set<Integer> ownerUids = mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(chain);
+ mDeps.destroyLiveTcpSocketsByOwnerUids(ownerUids);
+ }
+ }
+
@Override
public void setFirewallChainEnabled(final int chain, final boolean enable) {
enforceNetworkStackOrSettingsPermission();
@@ -11903,6 +12115,14 @@
} catch (ServiceSpecificException e) {
throw new IllegalStateException(e);
}
+
+ if (mDeps.isAtLeastU() && enable) {
+ try {
+ closeSocketsForFirewallChainLocked(chain);
+ } catch (ErrnoException | SocketException | InterruptedIOException e) {
+ Log.e(TAG, "Failed to close sockets after enabling chain (" + chain + "): " + e);
+ }
+ }
}
@Override
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 881c92d..f8285ed 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -177,6 +177,7 @@
private static final int MAX_EVENTS_LOGS = 40;
private final LocalLog mEventLog = new LocalLog(MAX_EVENTS_LOGS);
+ private final KeepaliveStatsTracker mKeepaliveStatsTracker = new KeepaliveStatsTracker();
/**
* Information about a managed keepalive.
*
@@ -209,8 +210,12 @@
this.mKi = Objects.requireNonNull(ki);
mCallback = ki.mCallback;
mUnderpinnedNetwork = underpinnedNetwork;
- if (autoOnOff && mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
- true /* defaultEnabled */)) {
+ // Reading DeviceConfig will check if the calling uid and calling package name are the
+ // same. Clear calling identity to align the calling uid and package
+ final boolean enabled = BinderUtils.withCleanCallingIdentity(
+ () -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
+ true /* defaultEnabled */));
+ if (autoOnOff && enabled) {
mAutomaticOnOffState = STATE_ENABLED;
if (null == ki.mFd) {
throw new IllegalArgumentException("fd can't be null with automatic "
@@ -421,6 +426,7 @@
public void handleStartKeepalive(Message message) {
final AutomaticOnOffKeepalive autoKi = (AutomaticOnOffKeepalive) message.obj;
mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
+ mKeepaliveStatsTracker.onStartKeepalive();
mKeepaliveTracker.handleStartKeepalive(autoKi.mKi);
// Add automatic on/off request into list to track its life cycle.
@@ -438,12 +444,14 @@
}
private void handleResumeKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
+ mKeepaliveStatsTracker.onResumeKeepalive();
mKeepaliveTracker.handleStartKeepalive(ki);
mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
}
private void handlePauseKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
mEventLog.log("Suspend keepalive " + ki.mCallback + " on " + ki.mNai);
+ mKeepaliveStatsTracker.onPauseKeepalive();
// TODO : mKT.handleStopKeepalive should take a KeepaliveInfo instead
mKeepaliveTracker.handleStopKeepalive(ki.getNai(), ki.getSlot(), SUCCESS_PAUSED);
}
@@ -467,6 +475,7 @@
private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
ensureRunningOnHandlerThread();
+ mKeepaliveStatsTracker.onStopKeepalive(autoKi.mAutomaticOnOffState != STATE_SUSPENDED);
autoKi.close();
if (null != autoKi.mAlarmListener) mAlarmManager.cancel(autoKi.mAlarmListener);
@@ -580,7 +589,7 @@
mKeepaliveTracker.dump(pw);
// Reading DeviceConfig will check if the calling uid and calling package name are the same.
// Clear calling identity to align the calling uid and package so that it won't fail if cts
- // would like to do the dump()
+ // would like to call dump()
final boolean featureEnabled = BinderUtils.withCleanCallingIdentity(
() -> mDependencies.isFeatureEnabled(AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
true /* defaultEnabled */));
@@ -837,7 +846,7 @@
*/
public boolean isFeatureEnabled(@NonNull final String name, final boolean defaultEnabled) {
return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
- defaultEnabled);
+ DeviceConfigUtils.TETHERING_MODULE_NAME, defaultEnabled);
}
/**
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 5d04632..fbe706c 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -237,9 +237,8 @@
/**
* Stop clatd.
*/
- public void stopClatd(String iface, String pfx96, String v4, String v6, int pid)
- throws IOException {
- native_stopClatd(iface, pfx96, v4, v6, pid);
+ public void stopClatd(int pid) throws IOException {
+ native_stopClatd(pid);
}
/**
@@ -843,9 +842,7 @@
Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
maybeStopBpf(mClatdTracker);
- mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
- mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
- mClatdTracker.pid);
+ mDeps.stopClatd(mClatdTracker.pid);
untagSocket(mClatdTracker.cookie);
Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
@@ -944,7 +941,6 @@
private static native int native_startClatd(FileDescriptor tunfd, FileDescriptor readsock6,
FileDescriptor writesock6, String iface, String pfx96, String v4, String v6)
throws IOException;
- private static native void native_stopClatd(String iface, String pfx96, String v4, String v6,
- int pid) throws IOException;
+ private static native void native_stopClatd(int pid) throws IOException;
private static native long native_getSocketCookie(FileDescriptor sock) throws IOException;
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 122ea1c..9039a14 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -61,6 +61,6 @@
*/
public void loadFlags(ConnectivityService.Dependencies deps, Context ctx) {
mNoRematchAllRequestsOnRegister = deps.isFeatureEnabled(
- ctx, NO_REMATCH_ALL_REQUESTS_ON_REGISTER, false /* defaultEnabled */);
+ ctx, NO_REMATCH_ALL_REQUESTS_ON_REGISTER);
}
}
diff --git a/framework/src/android/net/ConnectivityResources.java b/service/src/com/android/server/connectivity/ConnectivityResources.java
similarity index 67%
rename from framework/src/android/net/ConnectivityResources.java
rename to service/src/com/android/server/connectivity/ConnectivityResources.java
index 18f0de0..9089e4a 100644
--- a/framework/src/android/net/ConnectivityResources.java
+++ b/service/src/com/android/server/connectivity/ConnectivityResources.java
@@ -14,22 +14,16 @@
* limitations under the License.
*/
-package android.net;
-
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+package com.android.server.connectivity;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.List;
+import com.android.net.module.util.DeviceConfigUtils;
/**
* Utility to obtain the {@link com.android.server.ConnectivityService} {@link Resources}, in the
@@ -37,10 +31,6 @@
* @hide
*/
public class ConnectivityResources {
- private static final String RESOURCES_APK_INTENT =
- "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
- private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
-
@NonNull
private final Context mContext;
@@ -76,21 +66,10 @@
return mResourcesContext;
}
- final List<ResolveInfo> pkgs = mContext.getPackageManager()
- .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
- pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
- if (pkgs.size() > 1) {
- Log.wtf(ConnectivityResources.class.getSimpleName(),
- "More than one package found: " + pkgs);
- }
- if (pkgs.isEmpty()) {
- throw new IllegalStateException("No connectivity resource package found");
- }
-
+ final String resPkg = DeviceConfigUtils.getConnectivityResourcesPackageName(mContext);
final Context pkgContext;
try {
- pkgContext = mContext.createPackageContext(
- pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+ pkgContext = mContext.createPackageContext(resPkg, 0 /* flags */);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException("Resolved package not found", e);
}
diff --git a/service/src/com/android/server/connectivity/KeepaliveResourceUtil.java b/service/src/com/android/server/connectivity/KeepaliveResourceUtil.java
new file mode 100644
index 0000000..92b080d
--- /dev/null
+++ b/service/src/com/android/server/connectivity/KeepaliveResourceUtil.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
+
+import com.android.connectivity.resources.R;
+
+/**
+ * Utilities to fetch keepalive configuration from resources.
+ */
+public abstract class KeepaliveResourceUtil {
+
+ /**
+ * Read supported keepalive count for each transport type from overlay resource.
+ *
+ * @param context The context to read resource from.
+ * @return An array of supported keepalive count for each transport type.
+ */
+ @NonNull
+ public static int[] getSupportedKeepalives(@NonNull Context context) {
+ String[] res = null;
+ try {
+ final ConnectivityResources connRes = new ConnectivityResources(context);
+ res = connRes.get().getStringArray(R.array.config_networkSupportedKeepaliveCount);
+ } catch (Resources.NotFoundException unused) {
+ }
+ if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
+
+ final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1];
+ for (final String row : res) {
+ if (TextUtils.isEmpty(row)) {
+ throw new KeepaliveDeviceConfigurationException("Empty string");
+ }
+ final String[] arr = row.split(",");
+ if (arr.length != 2) {
+ throw new KeepaliveDeviceConfigurationException("Invalid parameter length");
+ }
+
+ int transport;
+ int supported;
+ try {
+ transport = Integer.parseInt(arr[0]);
+ supported = Integer.parseInt(arr[1]);
+ } catch (NumberFormatException e) {
+ throw new KeepaliveDeviceConfigurationException("Invalid number format");
+ }
+
+ if (!NetworkCapabilities.isValidTransport(transport)) {
+ throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
+ }
+
+ if (supported < 0) {
+ throw new KeepaliveDeviceConfigurationException(
+ "Invalid supported count " + supported + " for "
+ + NetworkCapabilities.transportNameOf(transport));
+ }
+ ret[transport] = supported;
+ }
+ return ret;
+ }
+
+ /**
+ * An exception thrown when the keepalive resource configuration is invalid.
+ */
+ public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
+ public KeepaliveDeviceConfigurationException(final String msg) {
+ super(msg);
+ }
+ }
+}
diff --git a/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
new file mode 100644
index 0000000..290d201
--- /dev/null
+++ b/service/src/com/android/server/connectivity/KeepaliveStatsTracker.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.metrics.DailykeepaliveInfoReported;
+import com.android.metrics.DurationForNumOfKeepalive;
+import com.android.metrics.DurationPerNumOfKeepalive;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO(b/273451360): Also track KeepaliveLifetimeForCarrier and DailykeepaliveInfoReported
+/**
+ * Tracks carrier and duration metrics of automatic on/off keepalives.
+ *
+ * <p>This class follows AutomaticOnOffKeepaliveTracker closely and its on*Keepalive methods needs
+ * to be called in a timely manner to keep the metrics accurate. It is also not thread-safe and all
+ * public methods must be called by the same thread, namely the ConnectivityService handler thread.
+ */
+public class KeepaliveStatsTracker {
+ private static final String TAG = KeepaliveStatsTracker.class.getSimpleName();
+
+ private final Dependencies mDependencies;
+ // List of duration stats metric where the index is the number of concurrent keepalives.
+ // Each DurationForNumOfKeepalive message stores a registered duration and an active duration.
+ // Registered duration is the total time spent with mNumRegisteredKeepalive == index.
+ // Active duration is the total time spent with mNumActiveKeepalive == index.
+ private final List<DurationForNumOfKeepalive.Builder> mDurationPerNumOfKeepalive =
+ new ArrayList<>();
+
+ private int mNumRegisteredKeepalive = 0;
+ private int mNumActiveKeepalive = 0;
+
+ // A timestamp of the most recent time the duration metrics was updated.
+ private long mTimestampSinceLastUpdateDurations;
+
+ /** Dependency class */
+ @VisibleForTesting
+ public static class Dependencies {
+ // Returns a timestamp with the time base of SystemClock.uptimeMillis to keep durations
+ // relative to start time and avoid timezone change.
+ public long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+ }
+
+ public KeepaliveStatsTracker() {
+ this(new Dependencies());
+ }
+
+ @VisibleForTesting
+ public KeepaliveStatsTracker(Dependencies dependencies) {
+ mDependencies = dependencies;
+ mTimestampSinceLastUpdateDurations = mDependencies.getUptimeMillis();
+ }
+
+ /** Ensures the list of duration metrics is large enough for number of registered keepalives. */
+ private void ensureDurationPerNumOfKeepaliveSize() {
+ if (mNumActiveKeepalive < 0 || mNumRegisteredKeepalive < 0) {
+ throw new IllegalStateException(
+ "Number of active or registered keepalives is negative");
+ }
+ if (mNumActiveKeepalive > mNumRegisteredKeepalive) {
+ throw new IllegalStateException(
+ "Number of active keepalives greater than registered keepalives");
+ }
+
+ while (mDurationPerNumOfKeepalive.size() <= mNumRegisteredKeepalive) {
+ final DurationForNumOfKeepalive.Builder durationForNumOfKeepalive =
+ DurationForNumOfKeepalive.newBuilder();
+ durationForNumOfKeepalive.setNumOfKeepalive(mDurationPerNumOfKeepalive.size());
+ durationForNumOfKeepalive.setKeepaliveRegisteredDurationsMsec(0);
+ durationForNumOfKeepalive.setKeepaliveActiveDurationsMsec(0);
+
+ mDurationPerNumOfKeepalive.add(durationForNumOfKeepalive);
+ }
+ }
+
+ /**
+ * Updates the durations metrics to the given time. This should always be called before making a
+ * change to mNumRegisteredKeepalive or mNumActiveKeepalive to keep the duration metrics
+ * correct.
+ *
+ * @param timeNow a timestamp obtained using Dependencies.getUptimeMillis
+ */
+ private void updateDurationsPerNumOfKeepalive(long timeNow) {
+ if (mDurationPerNumOfKeepalive.size() < mNumRegisteredKeepalive) {
+ Log.e(TAG, "Unexpected jump in number of registered keepalive");
+ }
+ ensureDurationPerNumOfKeepaliveSize();
+
+ final int durationIncrease = (int) (timeNow - mTimestampSinceLastUpdateDurations);
+ final DurationForNumOfKeepalive.Builder durationForNumOfRegisteredKeepalive =
+ mDurationPerNumOfKeepalive.get(mNumRegisteredKeepalive);
+
+ durationForNumOfRegisteredKeepalive.setKeepaliveRegisteredDurationsMsec(
+ durationForNumOfRegisteredKeepalive.getKeepaliveRegisteredDurationsMsec()
+ + durationIncrease);
+
+ final DurationForNumOfKeepalive.Builder durationForNumOfActiveKeepalive =
+ mDurationPerNumOfKeepalive.get(mNumActiveKeepalive);
+
+ durationForNumOfActiveKeepalive.setKeepaliveActiveDurationsMsec(
+ durationForNumOfActiveKeepalive.getKeepaliveActiveDurationsMsec()
+ + durationIncrease);
+
+ mTimestampSinceLastUpdateDurations = timeNow;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just started and is active. */
+ public void onStartKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumRegisteredKeepalive++;
+ mNumActiveKeepalive++;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been paused. */
+ public void onPauseKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumActiveKeepalive--;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been resumed. */
+ public void onResumeKeepalive() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumActiveKeepalive++;
+ }
+
+ /** Inform the KeepaliveStatsTracker a keepalive has just been stopped. */
+ public void onStopKeepalive(boolean wasActive) {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ mNumRegisteredKeepalive--;
+ if (wasActive) mNumActiveKeepalive--;
+ }
+
+ /**
+ * Builds and returns DailykeepaliveInfoReported proto.
+ */
+ public DailykeepaliveInfoReported buildKeepaliveMetrics() {
+ final long timeNow = mDependencies.getUptimeMillis();
+ updateDurationsPerNumOfKeepalive(timeNow);
+
+ final DurationPerNumOfKeepalive.Builder durationPerNumOfKeepalive =
+ DurationPerNumOfKeepalive.newBuilder();
+
+ mDurationPerNumOfKeepalive.forEach(
+ durationForNumOfKeepalive ->
+ durationPerNumOfKeepalive.addDurationForNumOfKeepalive(
+ durationForNumOfKeepalive));
+
+ final DailykeepaliveInfoReported.Builder dailyKeepaliveInfoReported =
+ DailykeepaliveInfoReported.newBuilder();
+
+ // TODO(b/273451360): fill all the other values and write to ConnectivityStatsLog.
+ dailyKeepaliveInfoReported.setDurationPerNumOfKeepalive(durationPerNumOfKeepalive);
+
+ return dailyKeepaliveInfoReported.build();
+ }
+
+ /** Resets the stored metrics but maintains the state of keepalives */
+ public void resetMetrics() {
+ mDurationPerNumOfKeepalive.clear();
+ ensureDurationPerNumOfKeepaliveSize();
+ }
+}
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 60485b3..cc226ce 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -37,7 +37,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.net.ConnectivityResources;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
import android.net.InvalidPacketException;
@@ -111,7 +110,7 @@
mTcpController = new TcpKeepaliveController(handler);
mContext = context;
- mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
+ mSupportedKeepalives = KeepaliveResourceUtil.getSupportedKeepalives(context);
final ConnectivityResources res = new ConnectivityResources(mContext);
mReservedPrivilegedSlots = res.get().getInteger(
@@ -570,7 +569,20 @@
} else if (reason == ERROR_STOP_REASON_UNINITIALIZED) {
throw new IllegalStateException("Unexpected stop reason: " + reason);
} else if (reason == ERROR_NO_SUCH_SLOT) {
- throw new IllegalStateException("No such slot: " + reason);
+ // There are multiple independent reasons a keepalive can stop. Some
+ // are software (e.g. the app stops the keepalive) and some are hardware
+ // (e.g. the SIM card gets removed). Therefore, there is a very low
+ // probability that both of these happen at the same time, which would
+ // result in the first stop attempt returning SUCCESS and the second
+ // stop attempt returning NO_SUCH_SLOT. Such a race condition can be
+ // ignored with a log.
+ // This should still be reported because if it happens with any frequency
+ // it probably means there is a bug where the system server is trying
+ // to use a non-existing hardware slot.
+ // TODO : separate the non-existing hardware slot from the case where
+ // there is no keepalive running on this slot.
+ Log.wtf(TAG, "Keepalive on slot " + slot + " can't be stopped : " + reason);
+ notifyErrorCallback(ki.mCallback, reason);
} else {
notifyErrorCallback(ki.mCallback, reason);
}
diff --git a/service/src/com/android/server/connectivity/LingerMonitor.java b/service/src/com/android/server/connectivity/LingerMonitor.java
index df34ce7..8503fcc 100644
--- a/service/src/com/android/server/connectivity/LingerMonitor.java
+++ b/service/src/com/android/server/connectivity/LingerMonitor.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.os.SystemClock;
import android.os.UserHandle;
diff --git a/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
index 58196f7..93018bb 100644
--- a/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
@@ -28,7 +28,6 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.net.ConnectivityResources;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
diff --git a/service/src/com/android/server/connectivity/NetworkDiagnostics.java b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
index 509110d..4f80d47 100644
--- a/service/src/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,12 @@
import static android.system.OsConstants.*;
+import static com.android.net.module.util.NetworkStackConstants.DNS_OVER_TLS_PORT;
+import static com.android.net.module.util.NetworkStackConstants.ICMP_HEADER_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.InetAddresses;
@@ -33,6 +39,7 @@
import android.system.Os;
import android.system.StructTimeval;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
@@ -172,7 +179,7 @@
}
}
- private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
+ private final Map<Pair<InetAddress, Integer>, Measurement> mIcmpChecks = new HashMap<>();
private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
new HashMap<>();
private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
@@ -205,17 +212,21 @@
mLinkProperties.addDnsServer(TEST_DNS6);
}
+ final int mtu = mLinkProperties.getMtu();
for (RouteInfo route : mLinkProperties.getRoutes()) {
if (route.getType() == RouteInfo.RTN_UNICAST && route.hasGateway()) {
InetAddress gateway = route.getGateway();
- prepareIcmpMeasurement(gateway);
+ // Use mtu in the route if exists. Otherwise, use the one in the link property.
+ final int routeMtu = route.getMtu();
+ prepareIcmpMeasurements(gateway, (routeMtu > 0) ? routeMtu : mtu);
if (route.isIPv6Default()) {
prepareExplicitSourceIcmpMeasurements(gateway);
}
}
}
+
for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
- prepareIcmpMeasurement(nameserver);
+ prepareIcmpMeasurements(nameserver, mtu);
prepareDnsMeasurement(nameserver);
// Unlike the DnsResolver which doesn't do certificate validation in opportunistic mode,
@@ -261,11 +272,50 @@
localAddr.getHostAddress(), inetSockAddr.getPort());
}
- private void prepareIcmpMeasurement(InetAddress target) {
- if (!mIcmpChecks.containsKey(target)) {
- Measurement measurement = new Measurement();
- measurement.thread = new Thread(new IcmpCheck(target, measurement));
- mIcmpChecks.put(target, measurement);
+ private static int getHeaderLen(@NonNull InetAddress target) {
+ // Convert IPv4 mapped v6 address to v4 if any.
+ try {
+ final InetAddress addr = InetAddress.getByAddress(target.getAddress());
+ // An ICMPv6 header is technically 4 bytes, but the implementation in IcmpCheck#run()
+ // will always fill in another 4 bytes padding in the v6 diagnostic packets, so the size
+ // before icmp data is always 8 bytes in the implementation of ICMP diagnostics for both
+ // v4 and v6 packets. Thus, it's fine to use the v4 header size in the length
+ // calculation.
+ if (addr instanceof Inet6Address) {
+ return IPV6_HEADER_LEN + ICMP_HEADER_LEN;
+ }
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Create InetAddress fail(" + target + "): " + e);
+ }
+
+ return IPV4_HEADER_MIN_LEN + ICMP_HEADER_LEN;
+ }
+
+ private void prepareIcmpMeasurements(@NonNull InetAddress target, int targetNetworkMtu) {
+ // Test with different size payload ICMP.
+ // 1. Test with 0 payload.
+ addPayloadIcmpMeasurement(target, 0);
+ final int header = getHeaderLen(target);
+ // 2. Test with full size MTU.
+ addPayloadIcmpMeasurement(target, targetNetworkMtu - header);
+ // 3. If v6, make another measurement with the full v6 min MTU, unless that's what
+ // was done above.
+ if ((target instanceof Inet6Address) && (targetNetworkMtu != IPV6_MIN_MTU)) {
+ addPayloadIcmpMeasurement(target, IPV6_MIN_MTU - header);
+ }
+ }
+
+ private void addPayloadIcmpMeasurement(@NonNull InetAddress target, int payloadLen) {
+ // This can happen if the there is no mtu filled(which is 0) in the link property.
+ // The value becomes negative after minus header length.
+ if (payloadLen < 0) return;
+
+ final Pair<InetAddress, Integer> lenTarget =
+ new Pair<>(target, Integer.valueOf(payloadLen));
+ if (!mIcmpChecks.containsKey(lenTarget)) {
+ final Measurement measurement = new Measurement();
+ measurement.thread = new Thread(new IcmpCheck(target, payloadLen, measurement));
+ mIcmpChecks.put(lenTarget, measurement);
}
}
@@ -276,7 +326,7 @@
Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target);
if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) {
Measurement measurement = new Measurement();
- measurement.thread = new Thread(new IcmpCheck(source, target, measurement));
+ measurement.thread = new Thread(new IcmpCheck(source, target, 0, measurement));
mExplicitSourceIcmpChecks.put(srcTarget, measurement);
}
}
@@ -334,8 +384,8 @@
ArrayList<Measurement> measurements = new ArrayList(totalMeasurementCount());
// Sort measurements IPv4 first.
- for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
- if (entry.getKey() instanceof Inet4Address) {
+ for (Map.Entry<Pair<InetAddress, Integer>, Measurement> entry : mIcmpChecks.entrySet()) {
+ if (entry.getKey().first instanceof Inet4Address) {
measurements.add(entry.getValue());
}
}
@@ -357,8 +407,8 @@
}
// IPv6 measurements second.
- for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
- if (entry.getKey() instanceof Inet6Address) {
+ for (Map.Entry<Pair<InetAddress, Integer>, Measurement> entry : mIcmpChecks.entrySet()) {
+ if (entry.getKey().first instanceof Inet6Address) {
measurements.add(entry.getValue());
}
}
@@ -489,8 +539,11 @@
private static final int PACKET_BUFSIZE = 512;
private final int mProtocol;
private final int mIcmpType;
+ private final int mPayloadSize;
+ // The length parameter is effectively the -s flag to ping/ping6 to specify the number of
+ // data bytes to be sent.
+ IcmpCheck(InetAddress source, InetAddress target, int length, Measurement measurement) {
- public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) {
super(source, target, measurement);
if (mAddressFamily == AF_INET6) {
@@ -502,12 +555,13 @@
mIcmpType = NetworkConstants.ICMPV4_ECHO_REQUEST_TYPE;
mMeasurement.description = "ICMPv4";
}
-
- mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
+ mPayloadSize = length;
+ mMeasurement.description += " payloadLength{" + mPayloadSize + "}"
+ + " dst{" + mTarget.getHostAddress() + "}";
}
- public IcmpCheck(InetAddress target, Measurement measurement) {
- this(null, target, measurement);
+ IcmpCheck(InetAddress target, int length, Measurement measurement) {
+ this(null, target, length, measurement);
}
@Override
@@ -523,9 +577,11 @@
mMeasurement.description += " src{" + socketAddressToString(mSocketAddress) + "}";
// Build a trivial ICMP packet.
- final byte[] icmpPacket = {
- (byte) mIcmpType, 0, 0, 0, 0, 0, 0, 0 // ICMP header
- };
+ // The v4 ICMP header ICMP_HEADER_LEN (which is 8) and v6 is only 4 bytes (4 bytes
+ // message body followed by header before the payload).
+ // Use 8 bytes for both v4 and v6 for simplicity.
+ final byte[] icmpPacket = new byte[ICMP_HEADER_LEN + mPayloadSize];
+ icmpPacket[0] = (byte) mIcmpType;
int count = 0;
mMeasurement.startTime = now();
@@ -675,7 +731,6 @@
private class DnsTlsCheck extends DnsUdpCheck {
private static final int TCP_CONNECT_TIMEOUT_MS = 2500;
private static final int TCP_TIMEOUT_MS = 2000;
- private static final int DNS_TLS_PORT = 853;
private static final int DNS_HEADER_SIZE = 12;
private final String mHostname;
@@ -714,7 +769,8 @@
final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
mMeasurement.startTime = now();
- sslSocket.connect(new InetSocketAddress(mTarget, DNS_TLS_PORT), TCP_CONNECT_TIMEOUT_MS);
+ sslSocket.connect(new InetSocketAddress(mTarget, DNS_OVER_TLS_PORT),
+ TCP_CONNECT_TIMEOUT_MS);
// Synchronous call waiting for the TLS handshake complete.
sslSocket.startHandshake();
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index cdc0aa9..8b0cb7c 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -30,7 +30,6 @@
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
-import android.net.ConnectivityResources;
import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 12e7d33..2245382 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -30,7 +30,6 @@
"cts-net-utils",
"ctstestrunner-axt",
"modules-utils-build",
- "ub-uiautomator",
],
libs: [
"android.test.runner",
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index 56d3cb5..ca3397b 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -34,7 +34,8 @@
<application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
- <activity android:name=".MyActivity"/>
+ <activity android:name=".MyActivity"
+ android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"/>
<service android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="true">
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
index b2e81ff..13bbab6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
@@ -19,18 +19,18 @@
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
import android.content.pm.PackageManager;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.UiAutomatorUtils;
+import com.android.compatibility.common.util.UiAutomatorUtils2;
import org.junit.After;
import org.junit.Assume;
@@ -84,7 +84,7 @@
final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
uiDevice.openNotification();
try {
- final UiObject2 uiObject = UiAutomatorUtils.waitFindObject(
+ final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(
By.text("Data warning"));
Assume.assumeNotNull(uiObject);
uiObject.wait(Until.clickable(true), 10_000L);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index c28ee64..73a6502 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -100,9 +100,6 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -114,6 +111,9 @@
import android.util.Range;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.modules.utils.build.SdkLevel;
@@ -154,7 +154,6 @@
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -809,26 +808,12 @@
mOldPrivateDnsSpecifier);
}
- // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
private void expectPrivateDnsHostname(final String hostname) throws Exception {
- final NetworkRequest request = new NetworkRequest.Builder()
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .build();
- final CountDownLatch latch = new CountDownLatch(1);
- final NetworkCallback callback = new NetworkCallback() {
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
- if (network.equals(mNetwork) &&
- Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
- latch.countDown();
- }
- }
- };
-
- registerNetworkCallback(request, callback);
-
- assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ // Wait for private DNS setting to propagate.
+ mCtsNetUtils.awaitPrivateDnsSetting("Test wait private DNS setting timeout",
+ network, hostname, false);
+ }
}
private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
diff --git a/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java b/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java
index 19e61c6..1a528b1 100644
--- a/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java
+++ b/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java
@@ -166,4 +166,15 @@
assertTrue(interval <= upperBoundSec);
}
}
+
+ /**
+ * Verify that cubic is used as the congestion control algorithm.
+ * (This repeats the VTS test, and is here for good performance of the internet as a whole.)
+ * TODO: revisit this once a better CC algorithm like BBR2 is available.
+ */
+ public void testCongestionControl() throws Exception {
+ String path = "/proc/sys/net/ipv4/tcp_congestion_control";
+ String value = mDevice.executeAdbCommand("shell", "cat", path).trim();
+ assertEquals(value, "cubic");
+ }
}
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index f9fe5b0..51ee074 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -61,7 +61,7 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
- data: [":ConnectivityChecker"],
+ data: [":ConnectivityTestPreparer"],
per_testcase_directory: true,
host_required: ["net-tests-utils-host-common"],
test_config_template: "AndroidTestTemplate.xml",
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 999614c..68e36ff 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -37,9 +37,6 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <!-- TODO (b/186093901): remove after fixing resource querying -->
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
-
<!-- This test also uses signature permissions through adopting the shell identity.
The permissions acquired that way include (probably not exhaustive) :
android.permission.MANAGE_TEST_NETWORKS
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index d2fb04a..8efa99f 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -28,7 +28,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
- <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
<target_preparer class="com.android.testutils.DisableConfigSyncTargetPreparer">
</target_preparer>
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 7662ba3..60befca 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -117,7 +117,7 @@
private static final int FAIL_RATE_PERCENTAGE = 100;
private static final int UNKNOWN_DETECTION_METHOD = 4;
private static final int FILTERED_UNKNOWN_DETECTION_METHOD = 0;
- private static final int CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT = 5000;
+ private static final int CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT = 10000;
private static final int DELAY_FOR_BROADCAST_IDLE = 30_000;
private static final Executor INLINE_EXECUTOR = x -> x.run();
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 6e06eb5..ee2f6bb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -225,6 +225,7 @@
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
+import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
@@ -278,6 +279,7 @@
// TODO(b/252972908): reset the original timer when aosp/2188755 is ramped up.
private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 30_000;
private static final int NO_CALLBACK_TIMEOUT_MS = 100;
+ private static final int NETWORK_REQUEST_TIMEOUT_MS = 3000;
private static final int SOCKET_TIMEOUT_MS = 100;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
@@ -1655,7 +1657,8 @@
final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
// Get number of supported concurrent keepalives for testing network.
- final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(mContext);
+ final int[] keepalivesPerTransport = runAsShell(NETWORK_SETTINGS,
+ () -> mCm.getSupportedKeepalives());
return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
keepalivesPerTransport, nc);
}
@@ -1670,6 +1673,9 @@
* keepalives is set to 0.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
+ // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
@Test
public void testKeepaliveWifiUnsupported() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1686,6 +1692,9 @@
}
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
+ // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
@Test
@RequiresDevice // Keepalive is not supported on virtual hardware
public void testCreateTcpKeepalive() throws Exception {
@@ -1894,6 +1903,9 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
+ // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
+ // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
@RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitWifi() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1944,6 +1956,9 @@
*/
@AppModeFull(reason = "Cannot request network in instant app mode")
@Test
+ // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
+ // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
@RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitTelephony() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
@@ -1990,6 +2005,9 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
+ // getSupportedKeepalives is available in updatable ConnectivityManager (S+)
+ // Also, this feature is not mainlined before S, and it's fine to skip on R- devices.
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) @ConnectivityModuleTest
@RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveUnprivileged() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -2112,7 +2130,12 @@
@AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps")
@Test
public void testSetAirplaneMode() throws Exception{
- final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
+ // Starting from T, wifi supports airplane mode enhancement which may not disconnect wifi
+ // when airplane mode is on. The actual behavior that the device will have could only be
+ // checked with hidden wifi APIs(see Settings.Secure.WIFI_APM_STATE). Thus, stop verifying
+ // wifi on T+ device.
+ final boolean verifyWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+ && !SdkLevel.isAtLeastT();
final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
// store the current state of airplane mode
final boolean isAirplaneModeEnabled = isAirplaneModeEnabled();
@@ -2123,7 +2146,7 @@
// Verify that networks are available as expected if wifi or cell is supported. Continue the
// test if none of them are supported since test should still able to verify the permission
// mechanism.
- if (supportWifi) {
+ if (verifyWifi) {
mCtsNetUtils.ensureWifiConnected();
registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
}
@@ -2147,7 +2170,7 @@
// Verify that the enabling airplane mode takes effect as expected to prevent flakiness
// caused by fast airplane mode switches. Ensure network lost before turning off
// airplane mode.
- if (supportWifi) waitForLost(wifiCb);
+ if (verifyWifi) waitForLost(wifiCb);
if (supportTelephony) waitForLost(telephonyCb);
// Verify we can disable Airplane Mode with correct permission:
@@ -2156,7 +2179,7 @@
// Verify that turning airplane mode off takes effect as expected.
// connectToCell only registers a request, it cannot / does not need to be called twice
mCtsNetUtils.ensureWifiConnected();
- if (supportWifi) waitForAvailable(wifiCb);
+ if (verifyWifi) waitForAvailable(wifiCb);
if (supportTelephony) waitForAvailable(telephonyCb);
} finally {
// Restore the previous state of airplane mode and permissions:
@@ -2552,6 +2575,14 @@
tetherUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ // To prevent WiFi-to-WiFi interruption while entering APM:
+ // - If WiFi is retained while entering APM, hotspot will also remain enabled.
+ // - If WiFi is off before APM or disabled while entering APM, hotspot will be
+ // disabled.
+ //
+ // To ensure hotspot always be disabled after enabling APM, disable wifi before
+ // enabling the hotspot.
+ mCtsNetUtils.disableWifi();
tetherUtils.startWifiTethering(tetherEventCallback);
// Update setting to verify the behavior.
@@ -2585,6 +2616,7 @@
ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
tetherUtils.unregisterTetheringEventCallback(tetherEventCallback);
tetherUtils.stopAllTethering();
+ mCtsNetUtils.ensureWifiConnected();
}
}
@@ -2948,13 +2980,13 @@
allowBadWifi();
- final Network cellNetwork = mCtsNetUtils.connectToCell();
- final Network wifiNetwork = prepareValidatedNetwork();
-
- registerDefaultNetworkCallback(defaultCb);
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
-
try {
+ final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final Network wifiNetwork = prepareValidatedNetwork();
+
+ registerDefaultNetworkCallback(defaultCb);
+ registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+
// Verify wifi is the default network.
defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
entry -> wifiNetwork.equals(entry.getNetwork()));
@@ -3396,15 +3428,15 @@
+ " uidFirewallRule=" + mCm.getUidFirewallRule(chain, Process.myUid()));
}
+ dstSock.receive(pkt);
+ assertArrayEquals(sendData, pkt.getData());
+
if (expectBlock) {
fail("Expect to be blocked by firewall but sending packet was not blocked:"
+ " chain=" + chain
+ " chainEnabled=" + mCm.getFirewallChainEnabled(chain)
+ " uidFirewallRule=" + mCm.getUidFirewallRule(chain, Process.myUid()));
}
-
- dstSock.receive(pkt);
- assertArrayEquals(sendData, pkt.getData());
}
private static final boolean EXPECT_PASS = false;
@@ -3523,6 +3555,103 @@
doTestFirewallBlocking(FIREWALL_CHAIN_OEM_DENY_3, DENYLIST);
}
+ private void assertSocketOpen(final Socket socket) throws Exception {
+ mCtsNetUtils.testHttpRequest(socket);
+ }
+
+ private void assertSocketClosed(final Socket socket) throws Exception {
+ try {
+ mCtsNetUtils.testHttpRequest(socket);
+ fail("Socket is expected to be closed");
+ } catch (SocketException expected) {
+ }
+ }
+
+ private static final boolean EXPECT_OPEN = false;
+ private static final boolean EXPECT_CLOSE = true;
+
+ private void doTestFirewallCloseSocket(final int chain, final int rule, final int targetUid,
+ final boolean expectClose) {
+ runWithShellPermissionIdentity(() -> {
+ // Firewall chain status will be restored after the test.
+ final boolean wasChainEnabled = mCm.getFirewallChainEnabled(chain);
+ final int previousUidFirewallRule = mCm.getUidFirewallRule(chain, targetUid);
+ final Socket socket = new Socket(TEST_HOST, HTTP_PORT);
+ socket.setSoTimeout(NETWORK_REQUEST_TIMEOUT_MS);
+ testAndCleanup(() -> {
+ mCm.setFirewallChainEnabled(chain, false /* enable */);
+ assertSocketOpen(socket);
+
+ try {
+ mCm.setUidFirewallRule(chain, targetUid, rule);
+ } catch (IllegalStateException ignored) {
+ // Removing match causes an exception when the rule entry for the uid does
+ // not exist. But this is fine and can be ignored.
+ }
+ mCm.setFirewallChainEnabled(chain, true /* enable */);
+
+ if (expectClose) {
+ assertSocketClosed(socket);
+ } else {
+ assertSocketOpen(socket);
+ }
+ }, /* cleanup */ () -> {
+ // Restore the global chain status
+ mCm.setFirewallChainEnabled(chain, wasChainEnabled);
+ }, /* cleanup */ () -> {
+ // Restore the uid firewall rule status
+ try {
+ mCm.setUidFirewallRule(chain, targetUid, previousUidFirewallRule);
+ } catch (IllegalStateException ignored) {
+ // Removing match causes an exception when the rule entry for the uid does
+ // not exist. But this is fine and can be ignored.
+ }
+ }, /* cleanup */ () -> {
+ socket.close();
+ });
+ }, NETWORK_SETTINGS);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketAllowlistChainAllow() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_ALLOW,
+ Process.myUid(), EXPECT_OPEN);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketAllowlistChainDeny() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_DENY,
+ Process.myUid(), EXPECT_CLOSE);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketAllowlistChainOtherUid() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_ALLOW,
+ Process.myUid() + 1, EXPECT_CLOSE);
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_DOZABLE, FIREWALL_RULE_DENY,
+ Process.myUid() + 1, EXPECT_CLOSE);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketDenylistChainAllow() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_ALLOW,
+ Process.myUid(), EXPECT_OPEN);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketDenylistChainDeny() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_DENY,
+ Process.myUid(), EXPECT_CLOSE);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) @ConnectivityModuleTest
+ public void testFirewallCloseSocketDenylistChainOtherUid() {
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_ALLOW,
+ Process.myUid() + 1, EXPECT_OPEN);
+ doTestFirewallCloseSocket(FIREWALL_CHAIN_STANDBY, FIREWALL_RULE_DENY,
+ Process.myUid() + 1, EXPECT_OPEN);
+ }
+
private void assumeTestSApis() {
// Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
// shims, and @IgnoreUpTo does not check that.
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 67bdd17..732a42b 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -392,7 +392,15 @@
}
// Setting the carrier up / down relies on TUNSETCARRIER which was added in kernel version 5.0.
- private fun assumeChangingCarrierSupported() = assumeTrue(isKernelVersionAtLeast("5.0.0"))
+ private fun assumeChangingCarrierSupported() {
+ assumeTrue(isKernelVersionAtLeast("5.0.0"))
+ }
+
+ // Configuring a tap interface without carrier relies on IFF_NO_CARRIER
+ // which was added in kernel version 6.0.
+ private fun assumeCreateInterfaceWithoutCarrierSupported() {
+ assumeTrue(isKernelVersionAtLeast("6.0.0"))
+ }
private fun isAdbOverEthernet(): Boolean {
// If no ethernet interface is available, adb is not connected over ethernet.
@@ -417,7 +425,7 @@
}
// WARNING: setting hasCarrier to false requires kernel support. Call
- // assumeChangingCarrierSupported() at the top of your test.
+ // assumeCreateInterfaceWithoutCarrierSupported() at the top of your test.
private fun createInterface(hasCarrier: Boolean = true): EthernetTestInterface {
val iface = EthernetTestInterface(
context,
@@ -791,15 +799,13 @@
@Test
fun testNetworkRequest_forInterfaceWhileTogglingCarrier() {
+ assumeCreateInterfaceWithoutCarrierSupported()
assumeChangingCarrierSupported()
val iface = createInterface(false /* hasCarrier */)
val cb = requestNetwork(ETH_REQUEST)
- // TUNSETCARRIER races with the bring up code, so the network *can* become available despite
- // it being "created with no carrier".
- // TODO(b/249611919): re-enable assertion once kernel supports IFF_NO_CARRIER.
- // cb.assertNeverAvailable()
+ cb.assertNeverAvailable()
iface.setCarrierEnabled(true)
cb.expect<Available>()
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index f935cef..cc0a5df 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -457,9 +457,8 @@
long newUidRxPackets = TrafficStats.getUidRxPackets(Os.getuid());
assertEquals(expectedTxByteDelta, newUidTxBytes - uidTxBytes);
- assertTrue(
- newUidRxBytes - uidRxBytes >= minRxByteDelta
- && newUidRxBytes - uidRxBytes <= maxRxByteDelta);
+ assertTrue("Not enough bytes", newUidRxBytes - uidRxBytes >= minRxByteDelta);
+ assertTrue("Too many bytes", newUidRxBytes - uidRxBytes <= maxRxByteDelta);
assertEquals(expectedTxPacketDelta, newUidTxPackets - uidTxPackets);
assertEquals(expectedRxPacketDelta, newUidRxPackets - uidRxPackets);
}
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 691ab99..17a9ca2 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -18,21 +18,18 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkUtils;
import android.net.cts.util.CtsNetUtils;
import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.test.AndroidTestCase;
-import java.util.ArrayList;
-
public class MultinetworkApiTest extends AndroidTestCase {
static {
@@ -75,26 +72,8 @@
super.tearDown();
}
- private Network[] getTestableNetworks() {
- final ArrayList<Network> testableNetworks = new ArrayList<Network>();
- for (Network network : mCM.getAllNetworks()) {
- final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
- if (nc != null
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- testableNetworks.add(network);
- }
- }
-
- assertTrue(
- "This test requires that at least one network be connected. " +
- "Please ensure that the device is connected to a network.",
- testableNetworks.size() >= 1);
- return testableNetworks.toArray(new Network[0]);
- }
-
public void testGetaddrinfo() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runGetaddrinfoCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -109,7 +88,7 @@
assertNull(mCM.getProcessDefaultNetwork());
assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
mCM.setProcessDefaultNetwork(null);
assertNull(mCM.getProcessDefaultNetwork());
@@ -128,7 +107,7 @@
mCM.setProcessDefaultNetwork(null);
}
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
NetworkUtils.bindProcessToNetwork(0);
assertNull(mCM.getBoundNetworkForProcess());
@@ -148,7 +127,7 @@
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
public void testSetsocknetwork() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runSetsocknetwork(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -158,7 +137,7 @@
}
public void testNativeDatagramTransmission() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runDatagramCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -181,7 +160,7 @@
public void testNetworkHandle() {
// Test Network -> NetworkHandle -> Network results in the same Network.
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
long networkHandle = network.getNetworkHandle();
Network newNetwork = Network.fromNetworkHandle(networkHandle);
assertEquals(newNetwork, network);
@@ -203,7 +182,7 @@
}
public void testResNApi() throws Exception {
- final Network[] testNetworks = getTestableNetworks();
+ final Network[] testNetworks = mCtsNetUtils.getTestableNetworks();
for (Network network : testNetworks) {
// Throws AssertionError directly in jni function if test fail.
@@ -229,7 +208,7 @@
// b/144521720
try {
mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
// Wait for private DNS setting to propagate.
mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
network, GOOGLE_PRIVATE_DNS_SERVER, true);
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index b535a8f..cf5fc50 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -29,9 +29,9 @@
import android.net.NattKeepalivePacketData
import android.net.Network
import android.net.NetworkAgent
-import android.net.NetworkAgentConfig
import android.net.NetworkAgent.INVALID_NETWORK
import android.net.NetworkAgent.VALID_NETWORK
+import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
@@ -46,21 +46,23 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_TEST
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkInfo
import android.net.NetworkProvider
import android.net.NetworkReleasedException
import android.net.NetworkRequest
import android.net.NetworkScore
-import android.net.RouteInfo
import android.net.QosCallback
-import android.net.QosCallbackException
import android.net.QosCallback.QosCallbackRegistrationException
+import android.net.QosCallbackException
import android.net.QosSession
import android.net.QosSessionAttributes
import android.net.QosSocketInfo
+import android.net.RouteInfo
import android.net.SocketKeepalive
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
import android.net.Uri
import android.net.VpnManager
import android.net.VpnTransportInfo
@@ -71,6 +73,7 @@
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
+import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
import android.system.OsConstants.IPPROTO_TCP
@@ -89,6 +92,7 @@
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
+import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
@@ -108,17 +112,6 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.argThat
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.timeout
-import org.mockito.Mockito.verify
import java.io.Closeable
import java.io.IOException
import java.net.DatagramSocket
@@ -136,6 +129,17 @@
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
@@ -168,6 +172,7 @@
// for modules other than Connectivity does not provide much value. Only run them in connectivity
// module MTS, so the tests only need to cover the case of an updated NetworkAgent.
@ConnectivityModuleTest
+@AppModeFull(reason = "Instant apps can't use NetworkAgent because it needs NETWORK_FACTORY'.")
class NetworkAgentTest {
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val REMOTE_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.2")
@@ -178,6 +183,7 @@
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Closeable? = null // either Socket or DatagramSocket
+ private val ifacesToCleanUp = mutableListOf<TestNetworkInterface>()
@Before
fun setUp() {
@@ -189,6 +195,7 @@
fun tearDown() {
agentsToCleanUp.forEach { it.unregister() }
callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
+ ifacesToCleanUp.forEach { it.fileDescriptor.close() }
qosTestSocket?.close()
mHandlerThread.quitSafely()
mHandlerThread.join()
@@ -269,7 +276,7 @@
removeCapability(NET_CAPABILITY_INTERNET)
addCapability(NET_CAPABILITY_NOT_SUSPENDED)
addCapability(NET_CAPABILITY_NOT_ROAMING)
- addCapability(NET_CAPABILITY_NOT_VPN)
+ if (!transports.contains(TRANSPORT_VPN)) addCapability(NET_CAPABILITY_NOT_VPN)
if (SdkLevel.isAtLeastS()) {
addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
}
@@ -304,7 +311,7 @@
context: Context = realContext,
specifier: String? = UUID.randomUUID().toString(),
initialConfig: NetworkAgentConfig? = null,
- expectedInitSignalStrengthThresholds: IntArray? = intArrayOf(),
+ expectedInitSignalStrengthThresholds: IntArray = intArrayOf(),
transports: IntArray = intArrayOf()
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
val callback = TestableNetworkCallback()
@@ -317,8 +324,7 @@
agent.register()
agent.markConnected()
agent.expectCallback<OnNetworkCreated>()
- agent.expectSignalStrengths(expectedInitSignalStrengthThresholds)
- agent.expectValidationBypassedStatus()
+ agent.expectPostConnectionCallbacks(expectedInitSignalStrengthThresholds)
callback.expectAvailableThenValidatedCallbacks(agent.network!!)
return agent to callback
}
@@ -336,6 +342,19 @@
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
}
+ private fun TestableNetworkAgent.expectPostConnectionCallbacks(
+ thresholds: IntArray = intArrayOf()
+ ) {
+ expectSignalStrengths(thresholds)
+ expectValidationBypassedStatus()
+ assertNoCallback()
+ }
+
+ private fun createTunInterface(): TestNetworkInterface = realContext.getSystemService(
+ TestNetworkManager::class.java)!!.createTunInterface(emptyList()).also {
+ ifacesToCleanUp.add(it)
+ }
+
fun assertLinkPropertiesEventually(
n: Network,
description: String,
@@ -964,13 +983,11 @@
.also { assertNotNull(agent.network?.bindSocket(it)) }
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackRegisterAndUnregister() {
validateQosCallbackRegisterAndUnregister(IPPROTO_TCP)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackRegisterAndUnregisterWithDatagramSocket() {
validateQosCallbackRegisterAndUnregister(IPPROTO_UDP)
@@ -1007,13 +1024,11 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnQosSession() {
validateQosCallbackOnQosSession(IPPROTO_TCP)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnQosSessionWithDatagramSocket() {
validateQosCallbackOnQosSession(IPPROTO_UDP)
@@ -1072,7 +1087,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnError() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1111,7 +1125,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackIdsAreMappedCorrectly() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1152,7 +1165,6 @@
}
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackWhenNetworkReleased() {
val (agent, qosTestSocket) = setupForQosSocket()
@@ -1194,7 +1206,6 @@
)
}
- @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testUnregisterAfterReplacement() {
// Keeps an eye on all test networks.
@@ -1291,8 +1302,12 @@
requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
val agent6 = createNetworkAgent(specifier = specifier6)
val network6 = agent6.register()
- // No callbacks are sent, so check the LinkProperties to see if the network has connected.
- assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+ if (SdkLevel.isAtLeastU()) {
+ agent6.expectCallback<OnNetworkCreated>()
+ } else {
+ // No callbacks are sent, so check LinkProperties to wait for the network to be created.
+ assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+ }
// unregisterAfterReplacement tears down the network immediately.
// Approximately check that this is the case by picking an unregister timeout that's longer
@@ -1301,8 +1316,9 @@
val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
agent6.unregisterAfterReplacement(timeoutMs)
agent6.expectCallback<OnNetworkUnwanted>()
- if (!SdkLevel.isAtLeastT()) {
+ if (!SdkLevel.isAtLeastT() || SdkLevel.isAtLeastU()) {
// Before T, onNetworkDestroyed is called even if the network was never created.
+ // On U+, the network was created by register(). Destroying it sends onNetworkDestroyed.
agent6.expectCallback<OnNetworkDestroyed>()
}
// Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
@@ -1375,4 +1391,101 @@
callback.expect<Available>(agent.network!!)
callback.eventuallyExpect<Lost> { it.network == agent.network }
}
+
+ fun doTestNativeNetworkCreation(expectCreatedImmediately: Boolean, transports: IntArray) {
+ val iface = createTunInterface()
+ val ifName = iface.interfaceName
+ val nc = makeTestNetworkCapabilities(ifName, transports).also {
+ if (transports.contains(TRANSPORT_VPN)) {
+ val sessionId = "NetworkAgentTest-${Process.myPid()}"
+ it.transportInfo = VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
+ /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false)
+ it.underlyingNetworks = listOf()
+ }
+ }
+ val lp = LinkProperties().apply {
+ interfaceName = ifName
+ addLinkAddress(LinkAddress("2001:db8::1/64"))
+ addRoute(RouteInfo(IpPrefix("2001:db8::/64"), null /* nextHop */, ifName))
+ addRoute(RouteInfo(IpPrefix("::/0"),
+ InetAddresses.parseNumericAddress("fe80::abcd"),
+ ifName))
+ }
+
+ // File a request containing the agent's specifier to receive callbacks and to ensure that
+ // the agent is not torn down due to being unneeded.
+ val request = makeTestNetworkRequest(specifier = ifName)
+ val requestCallback = TestableNetworkCallback()
+ requestNetwork(request, requestCallback)
+
+ val listenCallback = TestableNetworkCallback()
+ registerNetworkCallback(request, listenCallback)
+
+ // Register the NetworkAgent...
+ val agent = createNetworkAgent(realContext, initialNc = nc, initialLp = lp)
+ val network = agent.register()
+
+ // ... and then change the NetworkCapabilities and LinkProperties.
+ nc.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ agent.sendNetworkCapabilities(nc)
+ lp.addLinkAddress(LinkAddress("192.0.2.2/25"))
+ lp.addRoute(RouteInfo(IpPrefix("192.0.2.0/25"), null /* nextHop */, ifName))
+ agent.sendLinkProperties(lp)
+
+ requestCallback.assertNoCallback()
+ listenCallback.assertNoCallback()
+ if (!expectCreatedImmediately) {
+ agent.assertNoCallback()
+ agent.markConnected()
+ agent.expectCallback<OnNetworkCreated>()
+ } else {
+ agent.expectCallback<OnNetworkCreated>()
+ agent.markConnected()
+ }
+ agent.expectPostConnectionCallbacks()
+
+ // onAvailable must be called only when the network connects, and no other callbacks may be
+ // called before that happens. The callbacks report the state of the network as it was when
+ // it connected, so they reflect the NC and LP changes made after registration.
+ requestCallback.expect<Available>(network)
+ listenCallback.expect<Available>(network)
+
+ requestCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+ listenCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+
+ requestCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+ listenCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+
+ requestCallback.expect<BlockedStatus>()
+ listenCallback.expect<BlockedStatus>()
+
+ // Except for network validation, ensure no more callbacks are sent.
+ requestCallback.expectCaps(network) {
+ it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ listenCallback.expectCaps(network) {
+ it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ unregister(agent)
+ // Lost implicitly checks that no further callbacks happened after connect.
+ requestCallback.expect<Lost>(network)
+ listenCallback.expect<Lost>(network)
+ assertNull(mCM.getLinkProperties(network))
+ }
+
+ @Test
+ fun testNativeNetworkCreation_PhysicalNetwork() {
+ // On T and below, the native network is only created when the agent connects.
+ // Starting in U, the native network is created as soon as the agent is registered.
+ doTestNativeNetworkCreation(expectCreatedImmediately = SdkLevel.isAtLeastU(),
+ intArrayOf(TRANSPORT_CELLULAR))
+ }
+
+ @Test
+ fun testNativeNetworkCreation_Vpn() {
+ // VPN networks are always created as soon as the agent is registered.
+ doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index fcfecad..2704dd3 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -30,6 +30,7 @@
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
+import android.util.Log
import androidx.test.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -41,6 +42,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.Collections
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
@@ -64,10 +66,11 @@
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner::class)
class NetworkScoreTest {
+ private val TAG = javaClass.simpleName
private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
- private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+ private val mHandlerThread = HandlerThread("$TAG handler thread")
private val mHandler by lazy { Handler(mHandlerThread.looper) }
- private val agentsToCleanUp = mutableListOf<NetworkAgent>()
+ private val agentsToCleanUp = Collections.synchronizedList(mutableListOf<NetworkAgent>())
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
@Before
@@ -83,15 +86,18 @@
.addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler
)
}
+ Log.i(TAG, "Teardown on thread ${System.identityHashCode(Thread.currentThread())} " +
+ "cleaning up ${agentsToCleanUp.size} agents")
agentsToCleanUp.forEach {
+ Log.i(TAG, "Unregister agent for net ${it.network}")
it.unregister()
agentCleanUpCb.eventuallyExpect<CallbackEntry.Lost> { cb -> cb.network == it.network }
}
mCm.unregisterNetworkCallback(agentCleanUpCb)
+ callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
mHandlerThread.quitSafely()
mHandlerThread.join()
- callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
}
// Returns a networkCallback that sends onAvailable on the best network with TRANSPORT_TEST.
@@ -145,6 +151,8 @@
val agent = object : NetworkAgent(context, looper, "NetworkScore test agent", nc,
LinkProperties(), score, config, NetworkProvider(context, looper,
"NetworkScore test provider")) {}.also {
+ Log.i(TAG, "Add on thread ${System.identityHashCode(Thread.currentThread())} " +
+ "agent to clean up $it")
agentsToCleanUp.add(it)
}
runWithShellPermissionIdentity({ agent.register() }, MANAGE_TEST_NETWORKS)
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index f86c5cd..83b9b81 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -210,7 +210,6 @@
private long mStartTime;
private long mEndTime;
- private long mBytesRead;
private String mWriteSettingsMode;
private String mUsageStatsMode;
@@ -229,6 +228,7 @@
TrafficStats.setThreadStatsTag(NETWORK_TAG);
urlc = (HttpURLConnection) network.openConnection(url);
urlc.setConnectTimeout(TIMEOUT_MILLIS);
+ urlc.setReadTimeout(TIMEOUT_MILLIS);
urlc.setUseCaches(false);
// Disable compression so we generate enough traffic that assertWithinPercentage will
// not be affected by the small amount of traffic (5-10kB) sent by the test harness.
@@ -236,11 +236,10 @@
urlc.connect();
boolean ping = urlc.getResponseCode() == 200;
if (ping) {
- in = new InputStreamReader(
- (InputStream) urlc.getContent());
-
- mBytesRead = 0;
- while (in.read() != -1) ++mBytesRead;
+ in = new InputStreamReader((InputStream) urlc.getContent());
+ // Since the test doesn't really care about the precise amount of data, instead
+ // of reading all contents, just read few bytes at the beginning.
+ in.read();
}
} catch (Exception e) {
Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
@@ -379,7 +378,7 @@
.build(), callback);
synchronized (this) {
try {
- wait((int) (TIMEOUT_MILLIS * 1.2));
+ wait((int) (TIMEOUT_MILLIS * 2.4));
} catch (InterruptedException e) {
}
}
@@ -394,7 +393,7 @@
assertFalse(mNetworkInterfacesToTest[networkTypeIndex].getSystemFeature()
+ " is a reported system feature, "
+ "however no corresponding connected network interface was found or the attempt "
- + "to connect has timed out (timeout = " + TIMEOUT_MILLIS + "ms)."
+ + "to connect and read has timed out (timeout = " + (TIMEOUT_MILLIS * 2) + "ms)."
+ mNetworkInterfacesToTest[networkTypeIndex].getErrorMessage(), hasFeature);
return false;
}
@@ -800,7 +799,7 @@
// harness, which is untagged, won't cause a failure.
long firstTotal = resultsWithTraffic.get(0).total;
for (QueryResult queryResult : resultsWithTraffic) {
- assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 10);
+ assertWithinPercentage(queryResult + "", firstTotal, queryResult.total, 12);
}
// Expect to see no traffic when querying for any tag in tagsWithNoTraffic or any
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index db7f38c..9808137 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -73,8 +73,8 @@
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
import android.util.Log
+import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.PollingCheck
import com.android.compatibility.common.util.PropertyUtil
import com.android.modules.utils.build.SdkLevel.isAtLeastU
@@ -84,6 +84,8 @@
import com.android.networkstack.apishim.common.NsdShim
import com.android.testutils.ConnectivityModuleTest
import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.TestableNetworkAgent
@@ -129,12 +131,15 @@
// tried sequentially
private const val REGISTRATION_TIMEOUT_MS = 10_000L
private const val DBG = false
+private const val TEST_PORT = 12345
private val nsdShim = NsdShimImpl.newInstance()
@AppModeFull(reason = "Socket cannot bind in instant app mode")
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
@ConnectivityModuleTest
+@IgnoreUpTo(Build.VERSION_CODES.S_V2)
class NsdManagerTest {
// Rule used to filter CtsNetTestCasesMaxTargetSdkXX
@get:Rule
@@ -282,13 +287,17 @@
fun waitForServiceDiscovered(
serviceName: String,
+ serviceType: String,
expectedNetwork: Network? = null
): NsdServiceInfo {
- return expectCallbackEventually<ServiceFound> {
+ val serviceFound = expectCallbackEventually<ServiceFound> {
it.serviceInfo.serviceName == serviceName &&
(expectedNetwork == null ||
expectedNetwork == nsdShim.getNetwork(it.serviceInfo))
}.serviceInfo
+ // Discovered service types have a dot at the end
+ assertEquals("$serviceType.", serviceFound.serviceType)
+ return serviceFound
}
}
@@ -432,6 +441,13 @@
return agent
}
+ private fun makeTestServiceInfo(network: Network? = null) = NsdServiceInfo().also {
+ it.serviceType = serviceType
+ it.serviceName = serviceName
+ it.network = network
+ it.port = TEST_PORT
+ }
+
@After
fun tearDown() {
if (TestUtils.shouldTestTApis()) {
@@ -497,6 +513,10 @@
val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>(
REGISTRATION_TIMEOUT_MS).serviceInfo
+ // Only service name is included in ServiceRegistered callbacks
+ assertNull(registeredInfo.serviceType)
+ assertEquals(si.serviceName, registeredInfo.serviceName)
+
val discoveryRecord = NsdDiscoveryRecord()
// Test discovering without an Executor
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
@@ -505,12 +525,15 @@
discoveryRecord.expectCallback<DiscoveryStarted>()
// Expect a service record to be discovered
- val foundInfo = discoveryRecord.waitForServiceDiscovered(registeredInfo.serviceName)
+ val foundInfo = discoveryRecord.waitForServiceDiscovered(
+ registeredInfo.serviceName, serviceType)
// Test resolving without an Executor
val resolveRecord = NsdResolveRecord()
nsdManager.resolveService(foundInfo, resolveRecord)
val resolvedService = resolveRecord.expectCallback<ServiceResolved>().serviceInfo
+ assertEquals(".$serviceType", resolvedService.serviceType)
+ assertEquals(registeredInfo.serviceName, resolvedService.serviceName)
// Check Txt attributes
assertEquals(8, resolvedService.attributes.size)
@@ -538,9 +561,11 @@
registrationRecord.expectCallback<ServiceUnregistered>()
// Expect a callback for service lost
- discoveryRecord.expectCallbackEventually<ServiceLost> {
+ val lostCb = discoveryRecord.expectCallbackEventually<ServiceLost> {
it.serviceInfo.serviceName == serviceName
}
+ // Lost service types have a dot at the end
+ assertEquals("$serviceType.", lostCb.serviceInfo.serviceType)
// Register service again to see if NsdManager can discover it
val si2 = NsdServiceInfo()
@@ -554,7 +579,8 @@
// Expect a service record to be discovered (and filter the ones
// that are unrelated to this test)
- val foundInfo2 = discoveryRecord.waitForServiceDiscovered(registeredInfo2.serviceName)
+ val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
+ registeredInfo2.serviceName, serviceType)
// Resolve the service
val resolveRecord2 = NsdResolveRecord()
@@ -591,7 +617,7 @@
testNetwork1.network, Executor { it.run() }, discoveryRecord)
val foundInfo = discoveryRecord.waitForServiceDiscovered(
- serviceName, testNetwork1.network)
+ serviceName, serviceType, testNetwork1.network)
assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
// Rewind to ensure the service is not found on the other interface
@@ -638,6 +664,8 @@
val serviceDiscovered = discoveryRecord.expectCallback<ServiceFound>()
assertEquals(registeredInfo1.serviceName, serviceDiscovered.serviceInfo.serviceName)
+ // Discovered service types have a dot at the end
+ assertEquals("$serviceType.", serviceDiscovered.serviceInfo.serviceType)
assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered.serviceInfo))
// Unregister, then register the service back: it should be lost and found again
@@ -650,6 +678,7 @@
val registeredInfo2 = registerService(registrationRecord, si, executor)
val serviceDiscovered2 = discoveryRecord.expectCallback<ServiceFound>()
assertEquals(registeredInfo2.serviceName, serviceDiscovered2.serviceInfo.serviceName)
+ assertEquals("$serviceType.", serviceDiscovered2.serviceInfo.serviceType)
assertEquals(testNetwork1.network, nsdShim.getNetwork(serviceDiscovered2.serviceInfo))
// Teardown, then bring back up a network on the test interface: the service should
@@ -665,6 +694,7 @@
val newNetwork = newAgent.network ?: fail("Registered agent should have a network")
val serviceDiscovered3 = discoveryRecord.expectCallback<ServiceFound>()
assertEquals(registeredInfo2.serviceName, serviceDiscovered3.serviceInfo.serviceName)
+ assertEquals("$serviceType.", serviceDiscovered3.serviceInfo.serviceType)
assertEquals(newNetwork, nsdShim.getNetwork(serviceDiscovered3.serviceInfo))
} cleanupStep {
nsdManager.stopServiceDiscovery(discoveryRecord)
@@ -740,12 +770,12 @@
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
val foundInfo1 = discoveryRecord.waitForServiceDiscovered(
- serviceName, testNetwork1.network)
+ serviceName, serviceType, testNetwork1.network)
assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo1))
// Rewind as the service could be found on each interface in any order
discoveryRecord.nextEvents.rewind(0)
val foundInfo2 = discoveryRecord.waitForServiceDiscovered(
- serviceName, testNetwork2.network)
+ serviceName, serviceType, testNetwork2.network)
assertEquals(testNetwork2.network, nsdShim.getNetwork(foundInfo2))
nsdShim.resolveService(nsdManager, foundInfo1, Executor { it.run() }, resolveRecord)
@@ -790,7 +820,7 @@
testNetwork1.network, Executor { it.run() }, discoveryRecord)
// Expect that service is found on testNetwork1
val foundInfo = discoveryRecord.waitForServiceDiscovered(
- serviceName, testNetwork1.network)
+ serviceName, serviceType, testNetwork1.network)
assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
// Discover service on testNetwork2.
@@ -805,7 +835,7 @@
null as Network? /* network */, Executor { it.run() }, discoveryRecord3)
// Expect that service is found on testNetwork1
val foundInfo3 = discoveryRecord3.waitForServiceDiscovered(
- serviceName, testNetwork1.network)
+ serviceName, serviceType, testNetwork1.network)
assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo3))
} cleanupStep {
nsdManager.stopServiceDiscovery(discoveryRecord2)
@@ -835,7 +865,7 @@
nsdManager.discoverServices(
serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord
)
- val foundInfo = discoveryRecord.waitForServiceDiscovered(serviceNames)
+ val foundInfo = discoveryRecord.waitForServiceDiscovered(serviceNames, serviceType)
// Expect that resolving the service name works properly even service name contains
// non-standard characters.
@@ -985,7 +1015,7 @@
nsdShim.discoverServices(nsdManager, serviceType, NsdManager.PROTOCOL_DNS_SD,
testNetwork1.network, Executor { it.run() }, discoveryRecord)
val foundInfo = discoveryRecord.waitForServiceDiscovered(
- serviceName, testNetwork1.network)
+ serviceName, serviceType, testNetwork1.network)
// Register service callback and check the addresses are the same as network addresses
nsdShim.registerServiceInfoCallback(nsdManager, foundInfo, { it.run() }, cbRecord)
@@ -1030,6 +1060,52 @@
assertEquals(NsdManager.FAILURE_OPERATION_NOT_RUNNING, failedCb.errorCode)
}
+ @Test
+ fun testSubtypeAdvertisingAndDiscovery() {
+ val si = makeTestServiceInfo(network = testNetwork1.network)
+ // Test "_type._tcp.local,_subtype" syntax with the registration
+ si.serviceType = si.serviceType + ",_subtype"
+
+ val registrationRecord = NsdRegistrationRecord()
+
+ val baseTypeDiscoveryRecord = NsdDiscoveryRecord()
+ val subtypeDiscoveryRecord = NsdDiscoveryRecord()
+ val otherSubtypeDiscoveryRecord = NsdDiscoveryRecord()
+ tryTest {
+ registerService(registrationRecord, si)
+
+ // Test "_subtype._type._tcp.local" syntax with discovery. Note this is not
+ // "_subtype._sub._type._tcp.local".
+ nsdManager.discoverServices(serviceType,
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, baseTypeDiscoveryRecord)
+ nsdManager.discoverServices("_othersubtype.$serviceType",
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, otherSubtypeDiscoveryRecord)
+ nsdManager.discoverServices("_subtype.$serviceType",
+ NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, subtypeDiscoveryRecord)
+
+ subtypeDiscoveryRecord.waitForServiceDiscovered(
+ serviceName, serviceType, testNetwork1.network)
+ baseTypeDiscoveryRecord.waitForServiceDiscovered(
+ serviceName, serviceType, testNetwork1.network)
+ otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStarted>()
+ // The subtype callback was registered later but called, no need for an extra delay
+ otherSubtypeDiscoveryRecord.assertNoCallback(timeoutMs = 0)
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(baseTypeDiscoveryRecord)
+ nsdManager.stopServiceDiscovery(subtypeDiscoveryRecord)
+ nsdManager.stopServiceDiscovery(otherSubtypeDiscoveryRecord)
+
+ baseTypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ subtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ otherSubtypeDiscoveryRecord.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
/**
* Register a service and return its registration record.
*/
diff --git a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
index f0c87673..4854901 100644
--- a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
@@ -23,12 +23,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.PacProxyManager;
@@ -150,6 +152,9 @@
@AppModeFull(reason = "Instant apps can't bind sockets to localhost for a test proxy server")
@Test
public void testSetCurrentProxyScriptUrl() throws Exception {
+ // Devices without WebView/JavaScript cannot support PAC proxies
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW));
+
// Register a PacProxyInstalledListener
final TestPacProxyInstalledListener listener = new TestPacProxyInstalledListener();
final Executor executor = (Runnable r) -> r.run();
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index d817630..ce789fc 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -57,6 +57,8 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -68,6 +70,8 @@
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -422,7 +426,7 @@
.build();
}
- private void testHttpRequest(Socket s) throws IOException {
+ public void testHttpRequest(Socket s) throws IOException {
OutputStream out = s.getOutputStream();
InputStream in = s.getInputStream();
@@ -430,7 +434,9 @@
byte[] responseBytes = new byte[4096];
out.write(requestBytes);
in.read(responseBytes);
- assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
+ final String response = new String(responseBytes, "UTF-8");
+ assertTrue("Received unexpected response: " + response,
+ response.startsWith("HTTP/1.0 204 No Content\r\n"));
}
private Socket getBoundSocket(Network network, String host, int port) throws IOException {
@@ -506,17 +512,18 @@
* @throws InterruptedException If the thread is interrupted.
*/
public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
- @NonNull String server, boolean requiresValidatedServer) throws InterruptedException {
+ @Nullable String server, boolean requiresValidatedServer) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
- NetworkCallback callback = new NetworkCallback() {
+ final NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
return;
}
- if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
+ Log.i(TAG, "Set private DNS server to " + server);
+ if (network.equals(n) && Objects.equals(server, lp.getPrivateDnsServerName())) {
latch.countDown();
}
}
@@ -539,6 +546,27 @@
}
/**
+ * Get all testable Networks with internet capability.
+ */
+ public Network[] getTestableNetworks() {
+ final ArrayList<Network> testableNetworks = new ArrayList<Network>();
+ for (Network network : mCm.getAllNetworks()) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc != null
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ testableNetworks.add(network);
+ }
+ }
+
+ assertTrue("This test requires that at least one public Internet-providing"
+ + " network be connected. Please ensure that the device is connected to"
+ + " a network.",
+ testableNetworks.size() >= 1);
+ return testableNetworks.toArray(new Network[0]);
+ }
+
+ /**
* Receiver that captures the last connectivity change's network type and state. Recognizes
* both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
*/
diff --git a/tests/cts/tethering/AndroidTestTemplate.xml b/tests/cts/tethering/AndroidTestTemplate.xml
index c842c09..dd5b23e 100644
--- a/tests/cts/tethering/AndroidTestTemplate.xml
+++ b/tests/cts/tethering/AndroidTestTemplate.xml
@@ -20,12 +20,13 @@
<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="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
- <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.tethering.cts" />
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 3c1340d..67e1296 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -25,7 +25,6 @@
import android.content.ServiceConnection
import android.content.res.Resources
import android.net.ConnectivityManager
-import android.net.ConnectivityResources
import android.net.IDnsResolver
import android.net.INetd
import android.net.LinkProperties
@@ -50,11 +49,15 @@
import com.android.server.ConnectivityService
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
+import com.android.server.connectivity.ConnectivityResources
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.ProxyTracker
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.TestableNetworkCallback
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
@@ -73,9 +76,6 @@
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
import org.mockito.Spy
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlin.test.fail
const val SERVICE_BIND_TIMEOUT_MS = 5_000L
const val TEST_TIMEOUT_MS = 10_000L
@@ -215,8 +215,8 @@
inv.getArgument(2),
object : MultinetworkPolicyTracker.Dependencies() {
override fun getResourcesForActiveSubId(
- connResources: ConnectivityResources,
- activeSubId: Int
+ connResources: ConnectivityResources,
+ activeSubId: Int
) = resources
})
}.`when`(deps).makeMultinetworkPolicyTracker(any(), any(), any())
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 36b3356..8b286a0 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -26,7 +26,6 @@
"libandroid_net_frameworktests_util_jni",
"libbase",
"libbinder",
- "libbpf_bcc",
"libc++",
"libcrypto",
"libcutils",
diff --git a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
index cca0b66..cb3a315 100644
--- a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.content.res.Resources
-import android.net.ConnectivityResources
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.MAX_TRANSPORT
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
@@ -27,7 +26,9 @@
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.os.Build
import androidx.test.filters.SmallTest
-import com.android.internal.R
+import com.android.connectivity.resources.R
+import com.android.server.connectivity.ConnectivityResources
+import com.android.server.connectivity.KeepaliveResourceUtil
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.After
@@ -37,7 +38,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.any
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
@@ -53,14 +53,9 @@
class KeepaliveUtilsTest {
// Prepare mocked context with given resource strings.
- private fun getMockedContextWithStringArrayRes(
- id: Int,
- name: String,
- res: Array<out String?>?
- ): Context {
+ private fun getMockedContextWithStringArrayRes(id: Int, res: Array<out String?>?): Context {
val mockRes = mock(Resources::class.java)
doReturn(res).`when`(mockRes).getStringArray(eq(id))
- doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any())
return mock(Context::class.java).apply {
doReturn(mockRes).`when`(this).getResources()
@@ -79,10 +74,10 @@
try {
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
- "config_networkSupportedKeepaliveCount", res)
- KeepaliveUtils.getSupportedKeepalives(mockContext)
+ res)
+ KeepaliveResourceUtil.getSupportedKeepalives(mockContext)
fail("Expected KeepaliveDeviceConfigurationException")
- } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) {
+ } catch (expected: KeepaliveResourceUtil.KeepaliveDeviceConfigurationException) {
}
}
@@ -108,8 +103,8 @@
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
- "config_networkSupportedKeepaliveCount", validRes)
- val actual = KeepaliveUtils.getSupportedKeepalives(mockContext)
+ validRes)
+ val actual = KeepaliveResourceUtil.getSupportedKeepalives(mockContext)
assertArrayEquals(expectedValidRes, actual)
}
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index d189848..19fa41d 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -66,6 +66,7 @@
import android.os.Build;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import androidx.test.filters.SmallTest;
@@ -1151,4 +1152,33 @@
mCookieTagMap.updateEntry(new CookieTagMapKey(123), new CookieTagMapValue(456, 0x789));
assertDumpContains(getDump(), "cookie=123 tag=0x789 uid=456");
}
+
+ @Test
+ public void testGetUids() throws ErrnoException {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+ final long match0 = DOZABLE_MATCH | POWERSAVE_MATCH;
+ final long match1 = DOZABLE_MATCH | STANDBY_MATCH;
+ mUidOwnerMap.updateEntry(new S32(uid0), new UidOwnerValue(NULL_IIF, match0));
+ mUidOwnerMap.updateEntry(new S32(uid1), new UidOwnerValue(NULL_IIF, match1));
+
+ assertEquals(new ArraySet<>(List.of(uid0, uid1)),
+ mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(FIREWALL_CHAIN_DOZABLE));
+ assertEquals(new ArraySet<>(List.of(uid0)),
+ mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(FIREWALL_CHAIN_POWERSAVE));
+
+ assertEquals(new ArraySet<>(List.of(uid1)),
+ mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(FIREWALL_CHAIN_STANDBY));
+ assertEquals(new ArraySet<>(),
+ mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(FIREWALL_CHAIN_OEM_DENY_1));
+ }
+
+ @Test
+ public void testGetUidsIllegalArgument() {
+ final Class<IllegalArgumentException> expected = IllegalArgumentException.class;
+ assertThrows(expected,
+ () -> mBpfNetMaps.getUidsWithDenyRuleOnDenyListChain(FIREWALL_CHAIN_DOZABLE));
+ assertThrows(expected,
+ () -> mBpfNetMaps.getUidsWithAllowRuleOnAllowListChain(FIREWALL_CHAIN_OEM_DENY_1));
+ }
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 1cc0c89..904e4bd 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -30,6 +30,8 @@
import static android.Manifest.permission.NETWORK_SETUP_WIZARD;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
@@ -148,12 +150,14 @@
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
+import static com.android.server.ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION;
import static com.android.server.ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_PROFILE;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_VPN;
import static com.android.server.ConnectivityService.createDeliveryGroupKeyForConnectivityAction;
+import static com.android.server.ConnectivityService.makeNflogPrefix;
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
@@ -163,6 +167,7 @@
import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.FunctionalUtils.ignoreExceptions;
+import static com.android.testutils.HandlerUtils.visibleOnHandlerThread;
import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
import static com.android.testutils.MiscAsserts.assertContainsAll;
import static com.android.testutils.MiscAsserts.assertContainsExactly;
@@ -223,6 +228,8 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.UidFrozenStateChangedCallback;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -255,7 +262,6 @@
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
-import android.net.ConnectivityResources;
import android.net.ConnectivitySettingsManager;
import android.net.ConnectivityThread;
import android.net.DataStallReportParcelable;
@@ -371,7 +377,6 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LocationPermissionChecker;
@@ -382,12 +387,14 @@
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
+import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.DestroySocketsWrapper;
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
import com.android.server.connectivity.ApplicationSelfCertifiedNetworkCapabilities;
import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
+import com.android.server.connectivity.ConnectivityResources;
import com.android.server.connectivity.MultinetworkPolicyTracker;
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies;
import com.android.server.connectivity.Nat464Xlat;
@@ -502,7 +509,7 @@
// complete before callbacks are verified.
private static final int TEST_REQUEST_TIMEOUT_MS = 150;
- private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
+ private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 2_000;
private static final long TIMESTAMP = 1234L;
@@ -533,6 +540,10 @@
private static final int TEST_PACKAGE_UID = 123;
private static final int TEST_PACKAGE_UID2 = 321;
private static final int TEST_PACKAGE_UID3 = 456;
+
+ private static final int PACKET_WAKEUP_MASK = 0xffff0000;
+ private static final int PACKET_WAKEUP_MARK = 0x88880000;
+
private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
private static final String INTERFACE_NAME = "interface";
@@ -604,6 +615,8 @@
@Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
@Mock TetheringManager mTetheringManager;
@Mock BroadcastOptionsShim mBroadcastOptionsShim;
+ @Mock ActivityManager mActivityManager;
+ @Mock DestroySocketsWrapper mDestroySocketsWrapper;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -727,6 +740,7 @@
if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager;
if (Context.PAC_PROXY_SERVICE.equals(name)) return mPacProxyManager;
if (Context.TETHERING_SERVICE.equals(name)) return mTetheringManager;
+ if (Context.ACTIVITY_SERVICE.equals(name)) return mActivityManager;
return super.getSystemService(name);
}
@@ -855,7 +869,7 @@
@Override
public void sendStickyBroadcast(Intent intent, Bundle options) {
// Verify that delivery group policy APIs were used on U.
- if (SdkLevel.isAtLeastU() && CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ if (mDeps.isAtLeastU() && CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO,
NetworkInfo.class);
try {
@@ -1071,7 +1085,7 @@
}
private void onValidationRequested() throws Exception {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
verify(mNetworkMonitor).notifyNetworkConnectedParcel(any());
} else {
verify(mNetworkMonitor).notifyNetworkConnected(any(), any());
@@ -1910,10 +1924,15 @@
doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
doReturn(true).when(mResources)
.getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+ doReturn(PACKET_WAKEUP_MASK).when(mResources).getInteger(
+ R.integer.config_networkWakeupPacketMask);
+ doReturn(PACKET_WAKEUP_MARK).when(mResources).getInteger(
+ R.integer.config_networkWakeupPacketMark);
}
class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
final ConnectivityResources mConnRes;
+ final ArraySet<Pair<Long, Integer>> mEnabledChangeIds = new ArraySet<>();
ConnectivityServiceDependencies(final Context mockResContext) {
mConnRes = new ConnectivityResources(mockResContext);
@@ -1979,7 +1998,7 @@
@Override
public CarrierPrivilegeAuthenticator makeCarrierPrivilegeAuthenticator(
@NonNull final Context context, @NonNull final TelephonyManager tm) {
- return SdkLevel.isAtLeastT() ? mCarrierPrivilegeAuthenticator : null;
+ return mDeps.isAtLeastT() ? mCarrierPrivilegeAuthenticator : null;
}
@Override
@@ -2067,15 +2086,72 @@
}
@Override
- public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
+ public boolean isFeatureEnabled(Context context, String name) {
switch (name) {
case ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER:
return true;
+ case KEY_DESTROY_FROZEN_SOCKETS_VERSION:
+ return true;
default:
- return super.isFeatureEnabled(context, name, defaultEnabled);
+ return super.isFeatureEnabled(context, name);
}
}
+ public void setChangeIdEnabled(final boolean enabled, final long changeId, final int uid) {
+ final Pair<Long, Integer> data = new Pair<>(changeId, uid);
+ // mEnabledChangeIds is read on the handler thread and maybe the test thread, so
+ // make sure both threads see it before continuing.
+ visibleOnHandlerThread(mCsHandlerThread.getThreadHandler(), () -> {
+ if (enabled) {
+ mEnabledChangeIds.add(data);
+ } else {
+ mEnabledChangeIds.remove(data);
+ }
+ });
+ }
+
+ @Override
+ public boolean isChangeEnabled(final long changeId, final int uid) {
+ return mEnabledChangeIds.contains(new Pair<>(changeId, uid));
+ }
+
+ // In AOSP, build version codes are all over the place (e.g. at the time of this writing
+ // U == V). Define custom ones.
+ private static final int VERSION_UNMOCKED = -1;
+ private static final int VERSION_R = 1;
+ private static final int VERSION_S = 2;
+ private static final int VERSION_T = 3;
+ private static final int VERSION_U = 4;
+ private static final int VERSION_V = 5;
+ private static final int VERSION_MAX = VERSION_V;
+ private int mSdkLevel = VERSION_UNMOCKED;
+
+ private void setBuildSdk(final int sdkLevel) {
+ if (sdkLevel > VERSION_MAX) {
+ throw new IllegalArgumentException("setBuildSdk must not be called with"
+ + " Build.VERSION constants but Dependencies.VERSION_* constants");
+ }
+ visibleOnHandlerThread(mCsHandlerThread.getThreadHandler(), () -> mSdkLevel = sdkLevel);
+ }
+
+ @Override
+ public boolean isAtLeastS() {
+ return mSdkLevel == VERSION_UNMOCKED ? super.isAtLeastS()
+ : mSdkLevel >= VERSION_S;
+ }
+
+ @Override
+ public boolean isAtLeastT() {
+ return mSdkLevel == VERSION_UNMOCKED ? super.isAtLeastT()
+ : mSdkLevel >= VERSION_T;
+ }
+
+ @Override
+ public boolean isAtLeastU() {
+ return mSdkLevel == VERSION_UNMOCKED ? super.isAtLeastU()
+ : mSdkLevel >= VERSION_U;
+ }
+
@Override
public BpfNetMaps getBpfNetMaps(Context context, INetd netd) {
return mBpfNetMaps;
@@ -2148,6 +2224,35 @@
}
}
}
+
+ // Class to be mocked and used to verify destroy sockets methods call
+ public class DestroySocketsWrapper {
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids){}
+ public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids){}
+ }
+
+ @Override @SuppressWarnings("DirectInvocationOnMock")
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids) {
+ // Call mocked destroyLiveTcpSockets so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSockets(ranges, exemptUids);
+ }
+
+ @Override @SuppressWarnings("DirectInvocationOnMock")
+ public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids) {
+ // Call mocked destroyLiveTcpSocketsByOwnerUids so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSocketsByOwnerUids(ownerUids);
+ }
+
+ final ArrayTrackRecord<Long>.ReadHead mScheduledEvaluationTimeouts =
+ new ArrayTrackRecord<Long>().newReadHead();
+ @Override
+ public void scheduleEvaluationTimeout(@NonNull Handler handler,
+ @NonNull final Network network, final long delayMs) {
+ mScheduledEvaluationTimeouts.add(delayMs);
+ super.scheduleEvaluationTimeout(handler, network, delayMs);
+ }
}
private class AutomaticOnOffKeepaliveTrackerDependencies
@@ -3369,8 +3474,10 @@
// This test would be flaky with the default 120ms timer: that is short enough that
// lingered networks are torn down before assertions can be run. We don't want to mock the
// lingering timer to keep the WakeupMessage logic realistic: this has already proven useful
- // in detecting races.
- mService.mLingerDelayMs = 300;
+ // in detecting races. Furthermore, sometimes the test is running while Phenotype is running
+ // so hot that the test doesn't get the CPU for multiple hundreds of milliseconds, so this
+ // needs to be suitably long.
+ mService.mLingerDelayMs = 2_000;
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED)
@@ -3783,6 +3890,12 @@
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, callbacks);
+ if (mService.shouldCreateNetworksImmediately()) {
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertNull(eventOrder.poll());
+ }
+
// Connect a network, and file a request for it after it has come up, to ensure the nascent
// timer is cleared and the test does not have to wait for it. Filing the request after the
// network has come up is necessary because ConnectivityService does not appear to clear the
@@ -3790,7 +3903,12 @@
// connected.
// TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
mWiFiAgent.connectWithoutInternet();
- waitForIdle();
+ if (!mService.shouldCreateNetworksImmediately()) {
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } else {
+ waitForIdle();
+ assertNull(eventOrder.poll());
+ }
mCm.requestNetwork(request, callback);
callback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
@@ -3807,7 +3925,6 @@
// Disconnect the network and check that events happened in the right order.
mCm.unregisterNetworkCallback(callback);
- assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -5872,7 +5989,7 @@
doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
mPolicyTracker.reevaluate();
waitForIdle();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
// U+ ignore the setting and always actively prefers bad wifi
assertTrue(mService.mNetworkRanker.getConfiguration().activelyPreferBadWifi());
} else {
@@ -6000,10 +6117,13 @@
wifiCallback.assertNoCallback();
}
- public void doTestPreferBadWifi(final boolean preferBadWifi) throws Exception {
+ public void doTestPreferBadWifi(final boolean avoidBadWifi,
+ final boolean preferBadWifi,
+ @NonNull Predicate<Long> checkUnvalidationTimeout) throws Exception {
// Pretend we're on a carrier that restricts switching away from bad wifi, and
// depending on the parameter one that may indeed prefer bad wifi.
- doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ doReturn(avoidBadWifi ? 1 : 0).when(mResources)
+ .getInteger(R.integer.config_networkAvoidBadWifi);
doReturn(preferBadWifi ? 1 : 0).when(mResources)
.getInteger(R.integer.config_activelyPreferBadWifi);
mPolicyTracker.reevaluate();
@@ -6025,7 +6145,9 @@
mWiFiAgent.connect(false);
wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
- if (preferBadWifi) {
+ mDeps.mScheduledEvaluationTimeouts.poll(TIMEOUT_MS, t -> checkUnvalidationTimeout.test(t));
+
+ if (!avoidBadWifi && preferBadWifi) {
expectUnvalidationCheckWillNotify(mWiFiAgent, NotificationType.LOST_INTERNET);
mDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
} else {
@@ -6035,15 +6157,31 @@
}
@Test
- public void testPreferBadWifi_doNotPrefer() throws Exception {
+ public void testPreferBadWifi_doNotAvoid_doNotPrefer() throws Exception {
// Starting with U this mode is no longer supported and can't actually be tested
- assumeFalse(SdkLevel.isAtLeastU());
- doTestPreferBadWifi(false /* preferBadWifi */);
+ assumeFalse(mDeps.isAtLeastU());
+ doTestPreferBadWifi(false /* avoidBadWifi */, false /* preferBadWifi */,
+ timeout -> timeout < 14_000);
}
@Test
- public void testPreferBadWifi_doPrefer() throws Exception {
- doTestPreferBadWifi(true /* preferBadWifi */);
+ public void testPreferBadWifi_doNotAvoid_doPrefer() throws Exception {
+ doTestPreferBadWifi(false /* avoidBadWifi */, true /* preferBadWifi */,
+ timeout -> timeout > 14_000);
+ }
+
+ @Test
+ public void testPreferBadWifi_doAvoid_doNotPrefer() throws Exception {
+ // If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
+ doTestPreferBadWifi(true /* avoidBadWifi */, false /* preferBadWifi */,
+ timeout -> timeout < 14_000);
+ }
+
+ @Test
+ public void testPreferBadWifi_doAvoid_doPrefer() throws Exception {
+ // If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
+ doTestPreferBadWifi(true /* avoidBadWifi */, true /* preferBadWifi */,
+ timeout -> timeout < 14_000);
}
@Test
@@ -7593,7 +7731,9 @@
// Simple connection with initial LP should have updated ifaces.
mCellAgent.connect(false);
waitForIdle();
- expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
+ List<Network> allNetworks = mService.shouldCreateNetworksImmediately()
+ ? cellAndWifi() : onlyCell();
+ expectNotifyNetworkStatus(allNetworks, onlyCell(), MOBILE_IFNAME);
reset(mStatsManager);
// Verify change fields other than interfaces does not trigger a notification to NSS.
@@ -7902,9 +8042,13 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final int netId = mCellAgent.getNetwork().netId;
waitForIdle();
- verify(mMockDnsResolver, never()).setResolverConfiguration(any());
- verifyNoMoreInteractions(mMockDnsResolver);
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ } else {
+ verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+ }
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -7920,10 +8064,13 @@
mCellAgent.sendLinkProperties(cellLp);
mCellAgent.connect(false);
waitForIdle();
-
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
- // CS tells dnsresolver about the empty DNS config for this network.
+ if (!mService.shouldCreateNetworksImmediately()) {
+ // CS tells dnsresolver about the empty DNS config for this network.
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ }
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
+
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockDnsResolver);
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
@@ -8038,10 +8185,13 @@
mCm.requestNetwork(cellRequest, cellNetworkCallback);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final int netId = mCellAgent.getNetwork().netId;
waitForIdle();
- // CS tells netd about the empty DNS config for this network.
- verify(mMockDnsResolver, never()).setResolverConfiguration(any());
- verifyNoMoreInteractions(mMockDnsResolver);
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ } else {
+ verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+ }
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -8060,7 +8210,9 @@
mCellAgent.sendLinkProperties(cellLp);
mCellAgent.connect(false);
waitForIdle();
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
+ if (!mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ }
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
mResolverParamsParcelCaptor.capture());
ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue();
@@ -8071,6 +8223,7 @@
assertEquals(2, resolvrParams.tlsServers.length);
assertTrue(new ArraySet<>(resolvrParams.tlsServers).containsAll(
asList("2001:db8::1", "192.0.2.1")));
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockDnsResolver);
cellNetworkCallback.expect(AVAILABLE, mCellAgent);
cellNetworkCallback.expect(NETWORK_CAPS_UPDATED, mCellAgent);
@@ -9675,7 +9828,7 @@
final Set<Integer> excludedUids = new ArraySet<Integer>();
excludedUids.add(VPN_UID);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
// On T onwards, the corresponding SDK sandbox UID should also be excluded
excludedUids.add(toSdkSandboxUid(VPN_UID));
}
@@ -9723,7 +9876,7 @@
vpnDefaultCallbackAsUid.assertNoCallback();
excludedUids.add(uid);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
// On T onwards, the corresponding SDK sandbox UID should also be excluded
excludedUids.add(toSdkSandboxUid(uid));
}
@@ -10217,6 +10370,50 @@
}
}
+ private void doTestSetFirewallChainEnabledCloseSocket(final int chain,
+ final boolean isAllowList) throws Exception {
+ reset(mDestroySocketsWrapper);
+
+ mCm.setFirewallChainEnabled(chain, true /* enabled */);
+ final Set<Integer> uids =
+ new ArraySet<>(List.of(TEST_PACKAGE_UID, TEST_PACKAGE_UID2));
+ if (isAllowList) {
+ final Set<Range<Integer>> range = new ArraySet<>(
+ List.of(new Range<>(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE)));
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(range, uids);
+ } else {
+ verify(mDestroySocketsWrapper).destroyLiveTcpSocketsByOwnerUids(uids);
+ }
+
+ mCm.setFirewallChainEnabled(chain, false /* enabled */);
+ verifyNoMoreInteractions(mDestroySocketsWrapper);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testSetFirewallChainEnabledCloseSocket() throws Exception {
+ doReturn(new ArraySet<>(Arrays.asList(TEST_PACKAGE_UID, TEST_PACKAGE_UID2)))
+ .when(mBpfNetMaps)
+ .getUidsWithDenyRuleOnDenyListChain(anyInt());
+ doReturn(new ArraySet<>(Arrays.asList(TEST_PACKAGE_UID, TEST_PACKAGE_UID2)))
+ .when(mBpfNetMaps)
+ .getUidsWithAllowRuleOnAllowListChain(anyInt());
+
+ final boolean allowlist = true;
+ final boolean denylist = false;
+
+ doReturn(true).when(mBpfNetMaps).isFirewallAllowList(anyInt());
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_DOZABLE, allowlist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_POWERSAVE, allowlist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_RESTRICTED, allowlist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_LOW_POWER_STANDBY, allowlist);
+
+ doReturn(false).when(mBpfNetMaps).isFirewallAllowList(anyInt());
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_STANDBY, denylist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_OEM_DENY_1, denylist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_OEM_DENY_2, denylist);
+ doTestSetFirewallChainEnabledCloseSocket(FIREWALL_CHAIN_OEM_DENY_3, denylist);
+ }
+
private void doTestReplaceFirewallChain(final int chain) {
final int[] uids = new int[] {1001, 1002};
mCm.replaceFirewallChain(chain, uids);
@@ -10384,11 +10581,22 @@
return event;
}
+ private void verifyWakeupModifyInterface(String iface, boolean add) throws RemoteException {
+ if (add) {
+ verify(mMockNetd).wakeupAddInterface(eq(iface), anyString(), anyInt(),
+ anyInt());
+ } else {
+ verify(mMockNetd).wakeupDelInterface(eq(iface), anyString(), anyInt(),
+ anyInt());
+ }
+ }
+
private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
if (inOrder != null) {
return inOrder.verify(t);
} else {
- return verify(t);
+ // times(1) for consistency with the above. InOrder#verify always implies times(1).
+ return verify(t, times(1));
}
}
@@ -10402,7 +10610,7 @@
private void verifyClatdStart(@Nullable InOrder inOrder, @NonNull String iface, int netId,
@NonNull String nat64Prefix) throws Exception {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
verifyWithOrder(inOrder, mClatCoordinator)
.clatStart(eq(iface), eq(netId), eq(new IpPrefix(nat64Prefix)));
} else {
@@ -10412,7 +10620,7 @@
private void verifyNeverClatdStart(@Nullable InOrder inOrder, @NonNull String iface)
throws Exception {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
verifyNeverWithOrder(inOrder, mClatCoordinator).clatStart(eq(iface), anyInt(), any());
} else {
verifyNeverWithOrder(inOrder, mMockNetd).clatdStart(eq(iface), anyString());
@@ -10421,7 +10629,7 @@
private void verifyClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
throws Exception {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
verifyWithOrder(inOrder, mClatCoordinator).clatStop();
} else {
verifyWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
@@ -10430,13 +10638,28 @@
private void verifyNeverClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
throws Exception {
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
verifyNeverWithOrder(inOrder, mClatCoordinator).clatStop();
} else {
verifyNeverWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
}
}
+ private void expectNativeNetworkCreated(int netId, int permission, String iface,
+ InOrder inOrder) throws Exception {
+ verifyWithOrder(inOrder, mMockNetd).networkCreate(nativeNetworkConfigPhysical(netId,
+ permission));
+ verifyWithOrder(inOrder, mMockDnsResolver).createNetworkCache(eq(netId));
+ if (iface != null) {
+ verifyWithOrder(inOrder, mMockNetd).networkAddInterface(netId, iface);
+ }
+ }
+
+ private void expectNativeNetworkCreated(int netId, int permission, String iface)
+ throws Exception {
+ expectNativeNetworkCreated(netId, permission, iface, null /* inOrder */);
+ }
+
@Test
public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -10474,11 +10697,8 @@
int cellNetId = mCellAgent.getNetwork().netId;
waitForIdle();
- verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
- INetd.PERMISSION_NONE));
+ expectNativeNetworkCreated(cellNetId, INetd.PERMISSION_NONE, MOBILE_IFNAME);
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
- verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
mDeps.mReportedInterfaceHistory.newReadHead();
assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
@@ -10610,6 +10830,11 @@
clat.interfaceRemoved(CLAT_MOBILE_IFNAME);
networkCallback.assertNoCallback();
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(CLAT_MOBILE_IFNAME, false);
+ }
+
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mClatCoordinator);
verifyNoMoreInteractions(mMockDnsResolver);
@@ -10646,6 +10871,10 @@
assertRoutesAdded(cellNetId, stackedDefault);
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(CLAT_MOBILE_IFNAME, true);
+ }
+
// NAT64 prefix is removed. Expect that clat is stopped.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
cellNetId, PREFIX_OPERATION_REMOVED, kNat64PrefixString, 96));
@@ -10660,6 +10889,11 @@
cb -> cb.getLp().getStackedLinks().size() == 0);
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_MOBILE_IFNAME);
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(CLAT_MOBILE_IFNAME, false);
+ }
+
// Clean up.
mCellAgent.disconnect();
networkCallback.expect(LOST, mCellAgent);
@@ -10667,11 +10901,16 @@
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
verify(mMockNetd).setNetworkAllowlist(any());
} else {
verify(mMockNetd, never()).setNetworkAllowlist(any());
}
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(MOBILE_IFNAME, false);
+ }
+
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mClatCoordinator);
reset(mMockNetd);
@@ -10701,6 +10940,11 @@
verify(mMockNetd).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
// assertRoutesAdded sees all calls since last mMockNetd reset, so expect IPv6 routes again.
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default, stackedDefault);
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(MOBILE_IFNAME, true);
+ }
+
reset(mMockNetd);
reset(mClatCoordinator);
@@ -10709,14 +10953,24 @@
networkCallback.expect(LOST, mCellAgent);
networkCallback.assertNoCallback();
verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(CLAT_MOBILE_IFNAME, false);
+ }
+
verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
verify(mMockNetd).setNetworkAllowlist(any());
} else {
verify(mMockNetd, never()).setNetworkAllowlist(any());
}
+
+ if (mDeps.isAtLeastU()) {
+ verifyWakeupModifyInterface(MOBILE_IFNAME, false);
+ }
+
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mClatCoordinator);
@@ -11154,7 +11408,7 @@
mMockVpn.establish(lp, uid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, uid);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
// On T and above, VPN should have rules for null interface. Null Interface is a
// wildcard and this accepts traffic from all the interfaces.
// There are two expected invocations, one during the VPN initial
@@ -12143,7 +12397,7 @@
@Test
public void testUnderlyingNetworksWillBeSetInNetworkAgentInfoConstructor() throws Exception {
- assumeTrue(SdkLevel.isAtLeastT());
+ assumeTrue(mDeps.isAtLeastT());
final Network network1 = new Network(100);
final Network network2 = new Network(101);
final List<Network> underlyingNetworks = new ArrayList<>();
@@ -12469,12 +12723,11 @@
private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
throws Exception {
- InOrder inOrder = inOrder(mMockNetd);
- ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class);
+ InOrder inOrder = inOrder(mMockNetd, mDestroySocketsWrapper);
+ final Set<Integer> exemptUidSet = new ArraySet<>(List.of(exemptUid, Process.VPN_UID));
- inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
- exemptUidCaptor.capture());
- assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
if (add) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(
@@ -12486,9 +12739,8 @@
toUidRangeStableParcels(vpnRanges), PREFERENCE_ORDER_VPN));
}
- inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
- exemptUidCaptor.capture());
- assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
}
@Test
@@ -14993,7 +15245,7 @@
UserHandle testHandle,
TestNetworkCallback profileDefaultNetworkCallback,
TestNetworkCallback disAllowProfileDefaultNetworkCallback) throws Exception {
- final InOrder inOrder = inOrder(mMockNetd);
+ final InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellAgent.connect(true);
@@ -15009,8 +15261,16 @@
final TestNetworkAgentWrapper workAgent =
makeEnterpriseNetworkAgent(profileNetworkPreference.getPreferenceEnterpriseId());
+ if (mService.shouldCreateNetworksImmediately()) {
+ expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+ null /* iface */, inOrder);
+ }
if (connectWorkProfileAgentAhead) {
workAgent.connect(false);
+ if (!mService.shouldCreateNetworksImmediately()) {
+ expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+ null /* iface */, inOrder);
+ }
}
final TestOnCompleteListener listener = new TestOnCompleteListener();
@@ -15050,6 +15310,11 @@
if (!connectWorkProfileAgentAhead) {
workAgent.connect(false);
+ if (!mService.shouldCreateNetworksImmediately()) {
+ inOrder.verify(mMockNetd).networkCreate(
+ nativeNetworkConfigPhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM));
+ }
}
profileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
@@ -15058,8 +15323,6 @@
}
mSystemDefaultNetworkCallback.assertNoCallback();
mDefaultNetworkCallback.assertNoCallback();
- inOrder.verify(mMockNetd).networkCreate(
- nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
workAgent.getNetwork().netId,
uidRangeFor(testHandle, profileNetworkPreference),
@@ -16027,7 +16290,7 @@
mCellAgent.getNetwork().netId,
toUidRangeStableParcels(allowedRanges),
0 /* subPriority */);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{config1User});
} else {
inOrder.verify(mMockNetd, never()).setNetworkAllowlist(any());
@@ -16045,7 +16308,7 @@
mCellAgent.getNetwork().netId,
toUidRangeStableParcels(allowedRanges),
0 /* subPriority */);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{config2Users});
} else {
inOrder.verify(mMockNetd, never()).setNetworkAllowlist(any());
@@ -16076,7 +16339,7 @@
mCellAgent.getNetwork().netId,
allowAllUidRangesParcel,
0 /* subPriority */);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellAllAllowedConfig});
} else {
@@ -16095,7 +16358,7 @@
// making the order of the list undeterministic. Thus, verify this in order insensitive way.
final ArgumentCaptor<NativeUidRangeConfig[]> configsCaptor = ArgumentCaptor.forClass(
NativeUidRangeConfig[].class);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellAllAllowedConfig, enterpriseAllAllowedConfig));
@@ -16129,7 +16392,7 @@
mCellAgent.getNetwork().netId,
excludeAppRangesParcel,
0 /* subPriority */);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellExcludeAppConfig, enterpriseAllAllowedConfig));
@@ -16141,7 +16404,7 @@
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(cellAllAllowedConfig, enterpriseAllAllowedConfig));
@@ -16153,7 +16416,7 @@
// disconnects.
enterpriseAgent.disconnect();
waitForIdle();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellAllAllowedConfig});
} else {
@@ -16178,7 +16441,7 @@
List.of(prefBuilder.build()),
r -> r.run(), listener);
listener.expectOnComplete();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{});
} else {
inOrder.verify(mMockNetd, never()).setNetworkAllowlist(any());
@@ -16206,7 +16469,7 @@
mCellAgent.getNetwork().netId,
excludeAppRangesParcel,
0 /* subPriority */);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellExcludeAppConfig});
} else {
@@ -16230,7 +16493,7 @@
// making the order of the list undeterministic. Thus, verify this in order insensitive way.
final ArgumentCaptor<NativeUidRangeConfig[]> configsCaptor = ArgumentCaptor.forClass(
NativeUidRangeConfig[].class);
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(configsCaptor.capture());
assertContainsAll(List.of(configsCaptor.getValue()),
List.of(enterpriseAllAllowedConfig, cellExcludeAppConfig));
@@ -16241,7 +16504,7 @@
// Verify issuing with cellular set only when enterprise network disconnects.
enterpriseAgent.disconnect();
waitForIdle();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(
new NativeUidRangeConfig[]{cellExcludeAppConfig});
} else {
@@ -16250,7 +16513,7 @@
mCellAgent.disconnect();
waitForIdle();
- if (SdkLevel.isAtLeastU()) {
+ if (mDeps.isAtLeastU()) {
inOrder.verify(mMockNetd).setNetworkAllowlist(new NativeUidRangeConfig[]{});
} else {
inOrder.verify(mMockNetd, never()).setNetworkAllowlist(any());
@@ -16306,7 +16569,7 @@
profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
final TestOnCompleteListener listener = new TestOnCompleteListener();
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
mCm.setProfileNetworkPreferences(testHandle,
List.of(profileNetworkPreferenceBuilder.build()),
r -> r.run(), listener);
@@ -16414,7 +16677,7 @@
agent.getNetwork().getNetId(),
intToUidRangeStableParcels(uids),
preferenceOrder);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids200Parcel);
}
@@ -16422,7 +16685,7 @@
uids.add(400);
nc.setAllowedUids(uids);
agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(agent, c -> c.getAllowedUids().equals(uids));
} else {
cb.assertNoCallback();
@@ -16433,13 +16696,13 @@
agent.getNetwork().getNetId(),
intToUidRangeStableParcels(uids),
preferenceOrder);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids300400Parcel);
}
nc.setAllowedUids(uids);
agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(agent, c -> c.getAllowedUids().equals(uids));
inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids200Parcel);
} else {
@@ -16450,7 +16713,7 @@
uids.add(600);
nc.setAllowedUids(uids);
agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(agent, c -> c.getAllowedUids().equals(uids));
} else {
cb.assertNoCallback();
@@ -16459,7 +16722,7 @@
agent.getNetwork().getNetId(),
intToUidRangeStableParcels(uids),
preferenceOrder);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(uids600Parcel);
inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids300400Parcel);
}
@@ -16467,7 +16730,7 @@
uids.clear();
nc.setAllowedUids(uids);
agent.setNetworkCapabilities(nc, true /* sendToConnectivityService */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(agent, c -> c.getAllowedUids().isEmpty());
inOrder.verify(mMockNetd, times(1)).networkRemoveUidRangesParcel(uids600Parcel);
} else {
@@ -16520,7 +16783,7 @@
// Cell gets to set the service UID as access UID
ncb.setAllowedUids(serviceUidSet);
mEthernetAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
- if (SdkLevel.isAtLeastT() && hasAutomotiveFeature) {
+ if (mDeps.isAtLeastT() && hasAutomotiveFeature) {
cb.expectCaps(mEthernetAgent, c -> c.getAllowedUids().equals(serviceUidSet));
} else {
// S and no automotive feature must ignore access UIDs.
@@ -16573,7 +16836,7 @@
cb.expectAvailableThenValidatedCallbacks(mCellAgent);
ncb.setAllowedUids(serviceUidSet);
mCellAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(mCellAgent, c -> c.getAllowedUids().equals(serviceUidSet));
} else {
// S must ignore access UIDs.
@@ -16583,7 +16846,7 @@
// ...but not to some other UID. Rejection sets UIDs to the empty set
ncb.setAllowedUids(nonServiceUidSet);
mCellAgent.setNetworkCapabilities(ncb.build(), true /* sendToCS */);
- if (SdkLevel.isAtLeastT()) {
+ if (mDeps.isAtLeastT()) {
cb.expectCaps(mCellAgent, c -> c.getAllowedUids().isEmpty());
} else {
// S must ignore access UIDs.
@@ -17505,14 +17768,14 @@
@Test
public void testIgnoreValidationAfterRoamEnabled() throws Exception {
- final boolean enabled = !SdkLevel.isAtLeastT();
+ final boolean enabled = !mDeps.isAtLeastT();
doTestIgnoreValidationAfterRoam(5_000, enabled);
}
@Test
public void testShouldIgnoreValidationFailureAfterRoam() {
// Always disabled on T+.
- assumeFalse(SdkLevel.isAtLeastT());
+ assumeFalse(mDeps.isAtLeastT());
NetworkAgentInfo nai = fakeWifiNai(new NetworkCapabilities());
@@ -17570,18 +17833,93 @@
});
}
+ private void verifyMtuSetOnWifiInterface(int mtu) throws Exception {
+ verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+ }
+
+ private void verifyMtuNeverSetOnWifiInterface() throws Exception {
+ verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ }
+
+ private void verifyMtuSetOnWifiInterfaceOnlyUpToT(int mtu) throws Exception {
+ if (!mService.shouldCreateNetworksImmediately()) {
+ verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+ } else {
+ verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ }
+ }
+
+ private void verifyMtuSetOnWifiInterfaceOnlyStartingFromU(int mtu) throws Exception {
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+ } else {
+ verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ }
+ }
+
@Test
- public void testSendLinkPropertiesSetInterfaceMtu() throws Exception {
- final int mtu = 1327;
+ public void testSendLinkPropertiesSetInterfaceMtuBeforeConnect() throws Exception {
+ final int mtu = 1281;
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(WIFI_IFNAME);
lp.setMtu(mtu);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiAgent.sendLinkProperties(lp);
-
waitForIdle();
- verify(mMockNetd).interfaceSetMtu(eq(WIFI_IFNAME), eq(mtu));
+ verifyMtuSetOnWifiInterface(mtu);
+ reset(mMockNetd);
+
+ mWiFiAgent.connect(false /* validated */);
+ // Before U, the MTU is always (re-)applied when the network connects.
+ verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu);
+ }
+
+ @Test
+ public void testSendLinkPropertiesUpdateInterfaceMtuBeforeConnect() throws Exception {
+ final int mtu = 1327;
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(WIFI_IFNAME);
+ lp.setMtu(mtu);
+
+ // Registering an agent with an MTU only sets the MTU on U+.
+ mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ waitForIdle();
+ verifyMtuSetOnWifiInterfaceOnlyStartingFromU(mtu);
+ reset(mMockNetd);
+
+ // Future updates with the same MTU don't set the MTU even on T when it's not set initially.
+ mWiFiAgent.sendLinkProperties(lp);
+ waitForIdle();
+ verifyMtuNeverSetOnWifiInterface();
+
+ // Updating with a different MTU does work.
+ lp.setMtu(mtu + 1);
+ mWiFiAgent.sendLinkProperties(lp);
+ waitForIdle();
+ verifyMtuSetOnWifiInterface(mtu + 1);
+ reset(mMockNetd);
+
+ mWiFiAgent.connect(false /* validated */);
+ // Before U, the MTU is always (re-)applied when the network connects.
+ verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu + 1);
+ }
+
+ @Test
+ public void testSendLinkPropertiesUpdateInterfaceMtuAfterConnect() throws Exception {
+ final int mtu = 1327;
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(WIFI_IFNAME);
+ lp.setMtu(mtu);
+
+ mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiAgent.connect(false /* validated */);
+ verifyMtuNeverSetOnWifiInterface();
+
+ mWiFiAgent.sendLinkProperties(lp);
+ waitForIdle();
+ // The MTU is always (re-)applied when the network connects.
+ verifyMtuSetOnWifiInterface(mtu);
}
@Test
@@ -17592,14 +17930,15 @@
lp.setMtu(mtu);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mWiFiAgent.connect(false /* validated */);
+ verifyMtuSetOnWifiInterface(mtu);
+ reset(mMockNetd);
LinkProperties lp2 = new LinkProperties(lp);
lp2.setMtu(mtu2);
-
mWiFiAgent.sendLinkProperties(lp2);
-
waitForIdle();
- verify(mMockNetd).interfaceSetMtu(eq(WIFI_IFNAME), eq(mtu2));
+ verifyMtuSetOnWifiInterface(mtu2);
}
@Test
@@ -17610,10 +17949,13 @@
lp.setMtu(mtu);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
- mWiFiAgent.sendLinkProperties(new LinkProperties(lp));
+ mWiFiAgent.connect(false /* validated */);
+ verifyMtuSetOnWifiInterface(mtu);
+ reset(mMockNetd);
+ mWiFiAgent.sendLinkProperties(new LinkProperties(lp));
waitForIdle();
- verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ verifyMtuNeverSetOnWifiInterface();
}
@Test
@@ -17624,15 +17966,15 @@
lp.setMtu(mtu);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mWiFiAgent.connect(false /* validated */);
+ verifyMtuSetOnWifiInterface(mtu);
+ reset(mMockNetd);
- LinkProperties lp2 = new LinkProperties();
- assertNull(lp2.getInterfaceName());
- lp2.setMtu(mtu);
-
+ LinkProperties lp2 = new LinkProperties(lp);
+ lp2.setInterfaceName(null);
mWiFiAgent.sendLinkProperties(new LinkProperties(lp2));
-
waitForIdle();
- verify(mMockNetd, never()).interfaceSetMtu(any(), anyInt());
+ verifyMtuNeverSetOnWifiInterface();
}
@Test
@@ -17643,16 +17985,18 @@
lp.setMtu(mtu);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mWiFiAgent.connect(false /* validated */);
+ verifyMtuSetOnWifiInterface(mtu);
+ reset(mMockNetd);
final String ifaceName2 = WIFI_IFNAME + "_2";
- LinkProperties lp2 = new LinkProperties();
+ LinkProperties lp2 = new LinkProperties(lp);
lp2.setInterfaceName(ifaceName2);
- lp2.setMtu(mtu);
mWiFiAgent.sendLinkProperties(new LinkProperties(lp2));
-
waitForIdle();
- verify(mMockNetd).interfaceSetMtu(eq(ifaceName2), eq(mtu));
+ verify(mMockNetd, times(1)).interfaceSetMtu(eq(ifaceName2), eq(mtu));
+ verifyMtuNeverSetOnWifiInterface();
}
@Test
@@ -17664,4 +18008,108 @@
info.setExtraInfo("test_info");
assertEquals("0;2;test_info", createDeliveryGroupKeyForConnectivityAction(info));
}
+
+ @Test
+ public void testNetdWakeupAddInterfaceForWifiTransport() throws Exception {
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ mWiFiAgent.connect(false /* validated */);
+
+ final String expectedPrefix = makeNflogPrefix(WIFI_IFNAME,
+ mWiFiAgent.getNetwork().getNetworkHandle());
+ verify(mMockNetd).wakeupAddInterface(WIFI_IFNAME, expectedPrefix, PACKET_WAKEUP_MARK,
+ PACKET_WAKEUP_MASK);
+ }
+
+ @Test
+ public void testNetdWakeupAddInterfaceForCellularTransport() throws Exception {
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellAgent.connect(false /* validated */);
+
+ if (mDeps.isAtLeastU()) {
+ final String expectedPrefix = makeNflogPrefix(MOBILE_IFNAME,
+ mCellAgent.getNetwork().getNetworkHandle());
+ verify(mMockNetd).wakeupAddInterface(MOBILE_IFNAME, expectedPrefix, PACKET_WAKEUP_MARK,
+ PACKET_WAKEUP_MASK);
+ } else {
+ verify(mMockNetd, never()).wakeupAddInterface(eq(MOBILE_IFNAME), anyString(), anyInt(),
+ anyInt());
+ }
+ }
+
+ @Test
+ public void testNetdWakeupAddInterfaceForEthernetTransport() throws Exception {
+ final String ethernetIface = "eth42";
+
+ final LinkProperties ethLp = new LinkProperties();
+ ethLp.setInterfaceName(ethernetIface);
+ mEthernetAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET, ethLp);
+ mEthernetAgent.connect(false /* validated */);
+
+ verify(mMockNetd, never()).wakeupAddInterface(eq(ethernetIface), anyString(), anyInt(),
+ anyInt());
+ }
+
+ private static final int TEST_FROZEN_UID = 1000;
+ private static final int TEST_UNFROZEN_UID = 2000;
+
+ /**
+ * Send a UidFrozenStateChanged message to ConnectivityService. Verify that only the frozen UID
+ * gets passed to socketDestroy().
+ */
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ public void testFrozenUidSocketDestroy() throws Exception {
+ ArgumentCaptor<UidFrozenStateChangedCallback> callbackArg =
+ ArgumentCaptor.forClass(UidFrozenStateChangedCallback.class);
+
+ verify(mActivityManager).registerUidFrozenStateChangedCallback(any(),
+ callbackArg.capture());
+
+ final int[] uids = {TEST_FROZEN_UID, TEST_UNFROZEN_UID};
+ final int[] frozenStates = {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN};
+
+ callbackArg.getValue().onUidFrozenStateChanged(uids, frozenStates);
+
+ waitForIdle();
+
+ final Set<Integer> exemptUids = new ArraySet();
+ final UidRange frozenUidRange = new UidRange(TEST_FROZEN_UID, TEST_FROZEN_UID);
+ final Set<UidRange> ranges = Collections.singleton(frozenUidRange);
+
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
+ eq(exemptUids));
+ }
+
+ @Test
+ public void testDisconnectSuspendedNetworkStopClatd() throws Exception {
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_DUN)
+ .build();
+ mCm.requestNetwork(networkRequest, networkCallback);
+
+ final IpPrefix nat64Prefix = new IpPrefix(InetAddress.getByName("64:ff9b::"), 96);
+ NetworkCapabilities nc = new NetworkCapabilities().addCapability(NET_CAPABILITY_DUN);
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(MOBILE_IFNAME);
+ lp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+ lp.setNat64Prefix(nat64Prefix);
+ mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, lp, nc);
+ mCellAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* privateDnsProbeSent */);
+
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, mCellAgent.getNetwork().netId,
+ nat64Prefix.toString());
+
+ mCellAgent.suspend();
+ mCm.unregisterNetworkCallback(networkCallback);
+ mCellAgent.expectDisconnected();
+ waitForIdle();
+
+ verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
+ }
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 2ed989e..1997215 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -23,7 +23,7 @@
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
-import static com.android.server.NsdService.constructServiceType;
+import static com.android.server.NsdService.parseTypeAndSubtype;
import static com.android.testutils.ContextUtils.mockService;
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -77,6 +77,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
@@ -84,6 +85,7 @@
import com.android.server.NsdService.Dependencies;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
+import com.android.server.connectivity.mdns.MdnsSearchOptions;
import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
import com.android.server.connectivity.mdns.MdnsServiceInfo;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
@@ -104,6 +106,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -175,10 +178,10 @@
doReturn(true).when(mMockMDnsM).resolve(
anyInt(), anyString(), anyString(), anyString(), anyInt());
doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
- doReturn(mDiscoveryManager).when(mDeps).makeMdnsDiscoveryManager(any(), any());
- doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any());
- doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any());
-
+ doReturn(mDiscoveryManager).when(mDeps)
+ .makeMdnsDiscoveryManager(any(), any(), any());
+ doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any());
+ doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any());
mService = makeService();
}
@@ -908,7 +911,8 @@
listener.onServiceNameDiscovered(foundInfo);
verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
info.getServiceName().equals(SERVICE_NAME)
- && info.getServiceType().equals(SERVICE_TYPE)
+ // Service type in discovery callbacks has a dot at the end
+ && info.getServiceType().equals(SERVICE_TYPE + ".")
&& info.getNetwork().equals(network)));
final MdnsServiceInfo removedInfo = new MdnsServiceInfo(
@@ -927,7 +931,8 @@
listener.onServiceNameRemoved(removedInfo);
verify(discListener, timeout(TIMEOUT_MS)).onServiceLost(argThat(info ->
info.getServiceName().equals(SERVICE_NAME)
- && info.getServiceType().equals(SERVICE_TYPE)
+ // Service type in discovery callbacks has a dot at the end
+ && info.getServiceType().equals(SERVICE_TYPE + ".")
&& info.getNetwork().equals(network)));
client.stopServiceDiscovery(discListener);
@@ -967,6 +972,34 @@
}
@Test
+ @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
+ public void testDiscoveryWithMdnsDiscoveryManager_UsesSubtypes() {
+ final String typeWithSubtype = SERVICE_TYPE + ",_subtype";
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo regInfo = new NsdServiceInfo("Instance", typeWithSubtype);
+ final Network network = new Network(999);
+ regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
+ regInfo.setPort(12345);
+ regInfo.setNetwork(network);
+
+ final RegistrationListener regListener = mock(RegistrationListener.class);
+ client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
+ waitForIdle();
+ verify(mAdvertiser).addService(anyInt(), argThat(s ->
+ "Instance".equals(s.getServiceName())
+ && SERVICE_TYPE.equals(s.getServiceType())), eq("_subtype"));
+
+ final DiscoveryListener discListener = mock(DiscoveryListener.class);
+ client.discoverServices(typeWithSubtype, PROTOCOL, network, Runnable::run, discListener);
+ waitForIdle();
+ final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
+ ArgumentCaptor.forClass(MdnsSearchOptions.class);
+ verify(mDiscoveryManager).registerListener(eq(SERVICE_TYPE + ".local"), any(),
+ optionsCaptor.capture());
+ assertEquals(Collections.singletonList("subtype"), optionsCaptor.getValue().getSubtypes());
+ }
+
+ @Test
public void testResolutionWithMdnsDiscoveryManager() throws UnknownHostException {
setMdnsDiscoveryManagerEnabled();
@@ -974,7 +1007,7 @@
final ResolveListener resolveListener = mock(ResolveListener.class);
final Network network = new Network(999);
final String serviceType = "_nsd._service._tcp";
- final String constructedServiceType = "_nsd._sub._service._tcp.local";
+ final String constructedServiceType = "_service._tcp.local";
final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
@@ -982,10 +1015,14 @@
client.resolveService(request, resolveListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
+ final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
+ ArgumentCaptor.forClass(MdnsSearchOptions.class);
verify(mDiscoveryManager).registerListener(eq(constructedServiceType),
- listenerCaptor.capture(), argThat(options ->
- network.equals(options.getNetwork())
- && SERVICE_NAME.equals(options.getResolveInstanceName())));
+ listenerCaptor.capture(),
+ optionsCaptor.capture());
+ assertEquals(network, optionsCaptor.getValue().getNetwork());
+ // Subtypes are not used for resolution, only for discovery
+ assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
@@ -1009,7 +1046,7 @@
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture());
final NsdServiceInfo info = infoCaptor.getValue();
assertEquals(SERVICE_NAME, info.getServiceName());
- assertEquals("." + serviceType, info.getServiceType());
+ assertEquals("._service._tcp", info.getServiceType());
assertEquals(PORT, info.getPort());
assertTrue(info.getAttributes().containsKey("key"));
assertEquals(1, info.getAttributes().size());
@@ -1052,7 +1089,7 @@
final ArgumentCaptor<Integer> serviceIdCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mAdvertiser).addService(serviceIdCaptor.capture(),
- argThat(info -> matches(info, regInfo)));
+ argThat(info -> matches(info, regInfo)), eq(null) /* subtype */);
client.unregisterService(regListenerWithoutFeature);
waitForIdle();
@@ -1109,8 +1146,10 @@
waitForIdle();
// The advertiser is enabled for _type2 but not _type1
- verify(mAdvertiser, never()).addService(anyInt(), argThat(info -> matches(info, service1)));
- verify(mAdvertiser).addService(anyInt(), argThat(info -> matches(info, service2)));
+ verify(mAdvertiser, never()).addService(
+ anyInt(), argThat(info -> matches(info, service1)), eq(null) /* subtype */);
+ verify(mAdvertiser).addService(
+ anyInt(), argThat(info -> matches(info, service2)), eq(null) /* subtype */);
}
@Test
@@ -1122,7 +1161,7 @@
// final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
- verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any());
final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
regInfo.setHost(parseNumericAddress("192.0.2.123"));
@@ -1135,7 +1174,7 @@
verify(mSocketProvider).startMonitoringSockets();
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mAdvertiser).addService(idCaptor.capture(), argThat(info ->
- matches(info, regInfo)));
+ matches(info, regInfo)), eq(null) /* subtype */);
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
@@ -1161,7 +1200,7 @@
// final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
- verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any());
final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, "invalid_type");
regInfo.setHost(parseNumericAddress("192.0.2.123"));
@@ -1171,7 +1210,7 @@
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
- verify(mAdvertiser, never()).addService(anyInt(), any());
+ verify(mAdvertiser, never()).addService(anyInt(), any(), any());
verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
@@ -1186,7 +1225,7 @@
// final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor =
ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class);
- verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture());
+ verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any());
final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE);
regInfo.setHost(parseNumericAddress("192.0.2.123"));
@@ -1199,7 +1238,8 @@
final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
// Service name is truncated to 63 characters
verify(mAdvertiser).addService(idCaptor.capture(),
- argThat(info -> info.getServiceName().equals("a".repeat(63))));
+ argThat(info -> info.getServiceName().equals("a".repeat(63))),
+ eq(null) /* subtype */);
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
@@ -1217,7 +1257,7 @@
final ResolveListener resolveListener = mock(ResolveListener.class);
final Network network = new Network(999);
final String serviceType = "_nsd._service._tcp";
- final String constructedServiceType = "_nsd._sub._service._tcp.local";
+ final String constructedServiceType = "_service._tcp.local";
final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType);
@@ -1225,8 +1265,14 @@
client.resolveService(request, resolveListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
+ final ArgumentCaptor<MdnsSearchOptions> optionsCaptor =
+ ArgumentCaptor.forClass(MdnsSearchOptions.class);
verify(mDiscoveryManager).registerListener(eq(constructedServiceType),
- listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
+ listenerCaptor.capture(),
+ optionsCaptor.capture());
+ assertEquals(network, optionsCaptor.getValue().getNetwork());
+ // Subtypes are not used for resolution, only for discovery
+ assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes());
client.stopServiceResolution(resolveListener);
waitForIdle();
@@ -1241,16 +1287,22 @@
}
@Test
- public void testConstructServiceType() {
+ public void testParseTypeAndSubtype() {
final String serviceType1 = "test._tcp";
final String serviceType2 = "_test._quic";
- final String serviceType3 = "_123._udp.";
- final String serviceType4 = "_TEST._999._tcp.";
+ final String serviceType3 = "_test._quic,_test1,_test2";
+ final String serviceType4 = "_123._udp.";
+ final String serviceType5 = "_TEST._999._tcp.";
+ final String serviceType6 = "_998._tcp.,_TEST";
+ final String serviceType7 = "_997._tcp,_TEST";
- assertEquals(null, constructServiceType(serviceType1));
- assertEquals(null, constructServiceType(serviceType2));
- assertEquals("_123._udp", constructServiceType(serviceType3));
- assertEquals("_TEST._sub._999._tcp", constructServiceType(serviceType4));
+ assertNull(parseTypeAndSubtype(serviceType1));
+ assertNull(parseTypeAndSubtype(serviceType2));
+ assertNull(parseTypeAndSubtype(serviceType3));
+ assertEquals(new Pair<>("_123._udp", null), parseTypeAndSubtype(serviceType4));
+ assertEquals(new Pair<>("_999._tcp", "_TEST"), parseTypeAndSubtype(serviceType5));
+ assertEquals(new Pair<>("_998._tcp", "_TEST"), parseTypeAndSubtype(serviceType6));
+ assertEquals(new Pair<>("_997._tcp", "_TEST"), parseTypeAndSubtype(serviceType7));
}
@Test
@@ -1269,7 +1321,7 @@
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
- verify(mAdvertiser).addService(anyInt(), any());
+ verify(mAdvertiser).addService(anyInt(), any(), any());
// Verify the discovery uses MdnsDiscoveryManager
final DiscoveryListener discListener = mock(DiscoveryListener.class);
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 3eb1b26..9e0435d 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -38,7 +38,6 @@
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Resources;
-import android.net.ConnectivityResources;
import android.net.INetd;
import android.net.ISocketKeepaliveCallback;
import android.net.KeepalivePacketData;
@@ -63,6 +62,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.connectivity.resources.R;
import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -235,10 +235,8 @@
anyInt() /* pid */, anyInt() /* uid */);
ConnectivityResources.setResourcesContextForTest(mCtx);
final Resources mockResources = mock(Resources.class);
- doReturn(MOCK_RESOURCE_ID).when(mockResources).getIdentifier(any() /* name */,
- any() /* defType */, any() /* defPackage */);
doReturn(new String[] { "0,3", "3,3" }).when(mockResources)
- .getStringArray(MOCK_RESOURCE_ID);
+ .getStringArray(R.array.config_networkSupportedKeepaliveCount);
doReturn(mockResources).when(mCtx).getResources();
doReturn(mNetd).when(mDependencies).getNetd();
doReturn(mAlarmManager).when(mDependencies).getAlarmManager(any());
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index b651c33..4158663 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -313,8 +313,7 @@
* Stop clatd.
*/
@Override
- public void stopClatd(@NonNull String iface, @NonNull String pfx96, @NonNull String v4,
- @NonNull String v6, int pid) throws IOException {
+ public void stopClatd(int pid) throws IOException {
if (pid == -1) {
fail("unsupported arg: " + pid);
}
@@ -479,8 +478,7 @@
eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
- inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
- eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
+ inOrder.verify(mDeps).stopClatd(eq(CLATD_PID));
inOrder.verify(mCookieTagMap).deleteEntry(eq(COOKIE_TAG_KEY));
assertNull(coordinator.getClatdTrackerForTesting());
inOrder.verifyNoMoreInteractions();
diff --git a/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
new file mode 100644
index 0000000..d262255
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/KeepaliveStatsTrackerTest.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.metrics.DailykeepaliveInfoReported;
+import com.android.metrics.DurationForNumOfKeepalive;
+import com.android.metrics.DurationPerNumOfKeepalive;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+public class KeepaliveStatsTrackerTest {
+ private static final int TEST_UID = 1234;
+
+ private KeepaliveStatsTracker mKeepaliveStatsTracker;
+ @Mock KeepaliveStatsTracker.Dependencies mDependencies;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ setUptimeMillis(0);
+ mKeepaliveStatsTracker = new KeepaliveStatsTracker(mDependencies);
+ }
+
+ private void setUptimeMillis(long time) {
+ doReturn(time).when(mDependencies).getUptimeMillis();
+ }
+
+ /**
+ * Asserts that a DurationPerNumOfKeepalive contains expected values
+ *
+ * @param expectRegisteredDurations integer array where the index is the number of concurrent
+ * keepalives and the value is the expected duration of time that the tracker is in a state
+ * with the given number of keepalives registered.
+ * @param expectActiveDurations integer array where the index is the number of concurrent
+ * keepalives and the value is the expected duration of time that the tracker is in a state
+ * with the given number of keepalives active.
+ * @param resultDurationsPerNumOfKeepalive the DurationPerNumOfKeepalive message to assert.
+ */
+ private void assertDurationMetrics(
+ int[] expectRegisteredDurations,
+ int[] expectActiveDurations,
+ DurationPerNumOfKeepalive resultDurationsPerNumOfKeepalive) {
+ final int maxNumOfKeepalive = expectRegisteredDurations.length;
+ assertEquals(maxNumOfKeepalive, expectActiveDurations.length);
+ assertEquals(
+ maxNumOfKeepalive,
+ resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepaliveCount());
+ for (int numOfKeepalive = 0; numOfKeepalive < maxNumOfKeepalive; numOfKeepalive++) {
+ final DurationForNumOfKeepalive resultDurations =
+ resultDurationsPerNumOfKeepalive.getDurationForNumOfKeepalive(numOfKeepalive);
+
+ assertEquals(numOfKeepalive, resultDurations.getNumOfKeepalive());
+ assertEquals(
+ expectRegisteredDurations[numOfKeepalive],
+ resultDurations.getKeepaliveRegisteredDurationsMsec());
+ assertEquals(
+ expectActiveDurations[numOfKeepalive],
+ resultDurations.getKeepaliveActiveDurationsMsec());
+ }
+ }
+
+ private void assertDailyKeepaliveInfoReported(
+ DailykeepaliveInfoReported dailyKeepaliveInfoReported,
+ int[] expectRegisteredDurations,
+ int[] expectActiveDurations) {
+ // TODO(b/273451360) Assert these values when they are filled.
+ assertFalse(dailyKeepaliveInfoReported.hasKeepaliveLifetimePerCarrier());
+ assertFalse(dailyKeepaliveInfoReported.hasKeepaliveRequests());
+ assertFalse(dailyKeepaliveInfoReported.hasAutomaticKeepaliveRequests());
+ assertFalse(dailyKeepaliveInfoReported.hasDistinctUserCount());
+ assertTrue(dailyKeepaliveInfoReported.getUidList().isEmpty());
+
+ final DurationPerNumOfKeepalive resultDurations =
+ dailyKeepaliveInfoReported.getDurationPerNumOfKeepalive();
+ assertDurationMetrics(expectRegisteredDurations, expectActiveDurations, resultDurations);
+ }
+
+ @Test
+ public void testNoKeepalive() {
+ final int writeTime = 5000;
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // Expect that the durations are all in numOfKeepalive = 0.
+ final int[] expectRegisteredDurations = new int[] {writeTime};
+ final int[] expectActiveDurations = new int[] {writeTime};
+
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_startOnly() {
+ final int startTime = 1000;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is never stopped, expect the duration for numberOfKeepalive of 1 to range
+ // from startTime to writeTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_paused() {
+ final int startTime = 1000;
+ final int pauseTime = 2030;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is paused but not stopped, expect the registered duration for
+ // numberOfKeepalive of 1 to still range from startTime to writeTime while the active
+ // duration stops at pauseTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {startTime + (writeTime - pauseTime), pauseTime - startTime};
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_resumed() {
+ final int startTime = 1000;
+ final int pauseTime = 2030;
+ final int resumeTime = 3450;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is paused and resumed but not stopped, expect the registered duration for
+ // numberOfKeepalive of 1 to still range from startTime to writeTime while the active
+ // duration stops at pauseTime but resumes at resumeTime and stops at writeTime.
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + (resumeTime - pauseTime),
+ (pauseTime - startTime) + (writeTime - resumeTime)
+ };
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_stopped() {
+ final int startTime = 1000;
+ final int pauseTime = 2930;
+ final int resumeTime = 3452;
+ final int stopTime = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is now stopped, expect the registered duration for numberOfKeepalive of 1
+ // to now range from startTime to stopTime while the active duration stops at pauseTime but
+ // resumes at resumeTime and stops again at stopTime.
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + (resumeTime - pauseTime) + (writeTime - stopTime),
+ (pauseTime - startTime) + (stopTime - resumeTime)
+ };
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_pausedStopped() {
+ final int startTime = 1000;
+ final int pauseTime = 2930;
+ final int stopTime = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ false);
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // The keepalive is stopped while paused, expect the registered duration for
+ // numberOfKeepalive of 1 to range from startTime to stopTime while the active duration
+ // simply stops at pauseTime.
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {startTime + (writeTime - pauseTime), (pauseTime - startTime)};
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S P R P R P R S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testOneKeepalive_multiplePauses() {
+ final int startTime = 1000;
+ // Alternating timestamps of pause and resume
+ final int[] pauseResumeTimes = new int[] {1200, 1400, 1700, 2000, 2400, 2800};
+ final int stopTime = 4000;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ for (int i = 0; i < pauseResumeTimes.length; i++) {
+ setUptimeMillis(pauseResumeTimes[i]);
+ if (i % 2 == 0) {
+ mKeepaliveStatsTracker.onPauseKeepalive();
+ } else {
+ mKeepaliveStatsTracker.onResumeKeepalive();
+ }
+ }
+
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ final int[] expectRegisteredDurations =
+ new int[] {startTime + (writeTime - stopTime), stopTime - startTime};
+ final int[] expectActiveDurations =
+ new int[] {
+ startTime + /* sum of (Resume - Pause) */ (900) + (writeTime - stopTime),
+ (pauseResumeTimes[0] - startTime)
+ + /* sum of (Pause - Resume) */ (700)
+ + (stopTime - pauseResumeTimes[5])
+ };
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive1 S1 P1 R1 S1 W
+ * Keepalive2 S2 P2 R2 W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testTwoKeepalives() {
+ // The suffix 1/2 indicates which keepalive it is referring to.
+ final int startTime1 = 1000;
+ final int pauseTime1 = 1500;
+ final int startTime2 = 2000;
+ final int resumeTime1 = 2500;
+ final int pauseTime2 = 3000;
+ final int resumeTime2 = 3500;
+ final int stopTime1 = 4157;
+ final int writeTime = 5000;
+
+ setUptimeMillis(startTime1);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(pauseTime1);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(startTime2);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(resumeTime1);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(pauseTime2);
+ mKeepaliveStatsTracker.onPauseKeepalive();
+
+ setUptimeMillis(resumeTime2);
+ mKeepaliveStatsTracker.onResumeKeepalive();
+
+ setUptimeMillis(stopTime1);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // With two keepalives, the number of concurrent keepalives can vary from 0-2 depending on
+ // both keepalive states.
+ final int[] expectRegisteredDurations =
+ new int[] {
+ startTime1,
+ // 1 registered keepalive before keepalive2 starts and after keepalive1 stops.
+ (startTime2 - startTime1) + (writeTime - stopTime1),
+ // 2 registered keepalives between keepalive2 start and keepalive1 stop.
+ stopTime1 - startTime2
+ };
+
+ final int[] expectActiveDurations =
+ new int[] {
+ // 0 active keepalives when keepalive1 is paused before keepalive2 starts.
+ startTime1 + (startTime2 - pauseTime1),
+ // 1 active keepalive before keepalive1 is paused.
+ (pauseTime1 - startTime1)
+ // before keepalive1 is resumed and after keepalive2 starts.
+ + (resumeTime1 - startTime2)
+ // during keepalive2 is paused since keepalive1 has been resumed.
+ + (resumeTime2 - pauseTime2)
+ // after keepalive1 stops since keepalive2 has been resumed.
+ + (writeTime - stopTime1),
+ // 2 active keepalives before keepalive2 is paused and before keepalive1 stops.
+ (pauseTime2 - resumeTime1) + (stopTime1 - resumeTime2)
+ };
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+ }
+
+ /*
+ * Diagram of test (not to scale):
+ * Key: S - Start/Stop, P - Pause, R - Resume, W - Write
+ *
+ * Keepalive S W(reset+W) S W
+ * Timeline |------------------------------|
+ */
+ @Test
+ public void testResetMetrics() {
+ final int startTime = 1000;
+ final int writeTime = 5000;
+ final int stopTime = 7000;
+ final int writeTime2 = 10000;
+
+ setUptimeMillis(startTime);
+ mKeepaliveStatsTracker.onStartKeepalive();
+
+ setUptimeMillis(writeTime);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ // Same expect as testOneKeepalive_startOnly
+ final int[] expectRegisteredDurations = new int[] {startTime, writeTime - startTime};
+ final int[] expectActiveDurations = new int[] {startTime, writeTime - startTime};
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported,
+ expectRegisteredDurations,
+ expectActiveDurations);
+
+ // Reset metrics
+ mKeepaliveStatsTracker.resetMetrics();
+
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported2 =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+ // Expect the stored durations to be 0 but still contain the number of keepalive = 1.
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported2,
+ /* expectRegisteredDurations= */ new int[] {0, 0},
+ /* expectActiveDurations= */ new int[] {0, 0});
+
+ // Expect that the keepalive is still registered after resetting so it can be stopped.
+ setUptimeMillis(stopTime);
+ mKeepaliveStatsTracker.onStopKeepalive(/* wasActive= */ true);
+
+ setUptimeMillis(writeTime2);
+ final DailykeepaliveInfoReported dailyKeepaliveInfoReported3 =
+ mKeepaliveStatsTracker.buildKeepaliveMetrics();
+
+ final int[] expectRegisteredDurations2 =
+ new int[] {writeTime2 - stopTime, stopTime - writeTime};
+ final int[] expectActiveDurations2 =
+ new int[] {writeTime2 - stopTime, stopTime - writeTime};
+ assertDailyKeepaliveInfoReported(
+ dailyKeepaliveInfoReported3,
+ expectRegisteredDurations2,
+ expectActiveDurations2);
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
index 0d371fa..e6c0c83 100644
--- a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -34,7 +34,6 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.net.ConnectivityResources;
import android.net.IDnsResolver;
import android.net.INetd;
import android.net.LinkProperties;
diff --git a/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
index b52e8a8..f19ba4f 100644
--- a/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTest.kt
@@ -21,10 +21,8 @@
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
-import android.net.ConnectivityResources
import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI
import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE
-import com.android.server.connectivity.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
import android.os.Build
import android.os.Handler
import android.os.test.TestLooper
@@ -37,6 +35,7 @@
import com.android.connectivity.resources.R
import com.android.internal.util.test.FakeSettingsProvider
import com.android.modules.utils.build.SdkLevel
+import com.android.server.connectivity.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.After
diff --git a/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt
index 744c020..4c82c76 100644
--- a/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt
+++ b/tests/unit/java/com/android/server/connectivity/MultinetworkPolicyTrackerTestDependencies.kt
@@ -1,7 +1,6 @@
package com.android.server.connectivity
import android.content.res.Resources
-import android.net.ConnectivityResources
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
import android.provider.DeviceConfig.OnPropertiesChangedListener
diff --git a/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index f4b6464..d667662 100644
--- a/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -41,6 +42,8 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import libcore.util.EmptyArray;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,7 +63,8 @@
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
- private static final long NET_HANDLE = new Network(5391).getNetworkHandle();
+ private static final Network TEST_WIFI_NETWORK = new Network(5391);
+ private static final Network TEST_CELL_NETWORK = new Network(5832);
private static final byte[] MAC_ADDR =
{(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
@@ -78,6 +82,8 @@
public void setUp() {
mCm = mock(ConnectivityManager.class);
mService = new NetdEventListenerService(mCm);
+ doReturn(CAPABILITIES_WIFI).when(mCm).getNetworkCapabilities(TEST_WIFI_NETWORK);
+ doReturn(CAPABILITIES_CELL).when(mCm).getNetworkCapabilities(TEST_CELL_NETWORK);
}
@Test
@@ -111,19 +117,25 @@
wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
- wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", uids[8], v6, udp, EmptyArray.BYTE, srcIp6, dstIp6, sport, dport, now,
+ TEST_CELL_NETWORK);
String[] events2 = remove(listNetdEvent(), baseline);
- int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
+ int expectedLength2 = uids.length + 2; // +2 for the WakeupStats headers
assertEquals(expectedLength2, events2.length);
+
assertStringContains(events2[0], "WakeupStats");
- assertStringContains(events2[0], "wlan0");
- assertStringContains(events2[0], "0x800");
+ assertStringContains(events2[0], "rmnet0");
assertStringContains(events2[0], "0x86dd");
+
+ assertStringContains(events2[1], "WakeupStats");
+ assertStringContains(events2[1], "wlan0");
+ assertStringContains(events2[1], "0x800");
+ assertStringContains(events2[1], "0x86dd");
for (int i = 0; i < uids.length; i++) {
- String got = events2[i+1];
+ String got = events2[i + 2];
assertStringContains(got, "WakeupEvent");
- assertStringContains(got, "wlan0");
+ assertStringContains(got, ((i == 8) ? "rmnet0" : "wlan0"));
assertStringContains(got, "uid: " + uids[i]);
}
@@ -134,11 +146,13 @@
}
String[] events3 = remove(listNetdEvent(), baseline);
- int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
+ int expectedLength3 = BUFFER_LENGTH + 2; // +2 for the WakeupStats headers
assertEquals(expectedLength3, events3.length);
- assertStringContains(events2[0], "WakeupStats");
- assertStringContains(events2[0], "wlan0");
- for (int i = 1; i < expectedLength3; i++) {
+ assertStringContains(events3[0], "WakeupStats");
+ assertStringContains(events3[0], "rmnet0");
+ assertStringContains(events3[1], "WakeupStats");
+ assertStringContains(events3[1], "wlan0");
+ for (int i = 2; i < expectedLength3; i++) {
String got = events3[i];
assertStringContains(got, "WakeupEvent");
assertStringContains(got, "wlan0");
@@ -173,19 +187,24 @@
final int icmp6 = 58;
wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
- wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now,
+ TEST_CELL_NETWORK);
wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now);
- wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10008, v4, tcp, EmptyArray.BYTE, srcIp, dstIp, sport, dport, now,
+ TEST_CELL_NETWORK);
wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
- wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now,
+ TEST_CELL_NETWORK);
wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now);
wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
- wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now,
+ TEST_CELL_NETWORK);
wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
- wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v6, tcp, null, srcIp6, dstIp6, sport, dport, now,
+ TEST_CELL_NETWORK);
wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now);
String got = flushStatistics();
@@ -214,7 +233,7 @@
" >",
" l2_broadcast_count: 0",
" l2_multicast_count: 0",
- " l2_unicast_count: 5",
+ " l2_unicast_count: 3",
" no_uid_wakeups: 0",
" non_application_wakeups: 0",
" root_wakeups: 0",
@@ -499,8 +518,13 @@
}
void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
- String dstIp, int sport, int dport, long now) throws Exception {
- String prefix = NET_HANDLE + ":" + iface;
+ String dstIp, int sport, int dport, long now) {
+ wakeupEvent(iface, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now, TEST_WIFI_NETWORK);
+ }
+
+ void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+ String dstIp, int sport, int dport, long now, Network network) {
+ String prefix = network.getNetworkHandle() + ":" + iface;
mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
}
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index e038c44..a27a0bf 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -51,7 +51,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index c599d9d..2d2819c 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -25,6 +25,9 @@
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -40,7 +43,6 @@
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV4;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_IPV6;
-import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.UserHandle.PER_USER_RANGE;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
@@ -55,7 +57,6 @@
import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_ESP;
import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV6_UDP;
import static com.android.testutils.Cleanup.testAndCleanup;
-import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
@@ -142,6 +143,7 @@
import android.net.ipsec.ike.exceptions.IkeNonProtocolException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.ipsec.ike.exceptions.IkeTimeoutException;
+import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -165,6 +167,7 @@
import android.util.Pair;
import android.util.Range;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -172,14 +175,13 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.DeviceIdleInternal;
import com.android.server.IpSecService;
import com.android.server.VpnTestBase;
import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -196,6 +198,7 @@
import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.StringWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -213,7 +216,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Tests for {@link Vpn}.
@@ -221,9 +225,8 @@
* Build, install and run with:
* runtest frameworks-net -c com.android.server.connectivity.VpnTest
*/
-@RunWith(DevSdkIgnoreRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
-@IgnoreUpTo(S_V2)
public class VpnTest extends VpnTestBase {
private static final String TAG = "VpnTest";
@@ -434,11 +437,6 @@
.thenReturn(ikeSession);
}
- @After
- public void tearDown() throws Exception {
- doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
- }
-
private <T> void mockService(Class<T> clazz, String name, T service) {
doReturn(service).when(mContext).getSystemService(name);
doReturn(name).when(mContext).getSystemServiceName(clazz);
@@ -1569,6 +1567,11 @@
}
private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception {
+ return triggerOnAvailableAndGetCallback(new NetworkCapabilities.Builder().build());
+ }
+
+ private NetworkCallback triggerOnAvailableAndGetCallback(
+ @NonNull final NetworkCapabilities caps) throws Exception {
final ArgumentCaptor<NetworkCallback> networkCallbackCaptor =
ArgumentCaptor.forClass(NetworkCallback.class);
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
@@ -1585,7 +1588,7 @@
// if NetworkCapabilities and LinkProperties of underlying network will be sent/cleared or
// not.
// See verifyVpnManagerEvent().
- cb.onCapabilitiesChanged(TEST_NETWORK, new NetworkCapabilities());
+ cb.onCapabilitiesChanged(TEST_NETWORK, caps);
cb.onLinkPropertiesChanged(TEST_NETWORK, new LinkProperties());
return cb;
}
@@ -1909,12 +1912,15 @@
private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6) throws Exception {
- return verifySetupPlatformVpn(vpnProfile, ikeConfig, mtuSupportsIpv6,
- false /* areLongLivedTcpConnectionsExpensive */);
+ return verifySetupPlatformVpn(vpnProfile, ikeConfig,
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
+ mtuSupportsIpv6, false /* areLongLivedTcpConnectionsExpensive */);
}
private PlatformVpnSnapshot verifySetupPlatformVpn(VpnProfile vpnProfile,
- IkeSessionConfiguration ikeConfig, boolean mtuSupportsIpv6,
+ IkeSessionConfiguration ikeConfig,
+ @NonNull final NetworkCapabilities underlyingNetworkCaps,
+ boolean mtuSupportsIpv6,
boolean areLongLivedTcpConnectionsExpensive) throws Exception {
if (!mtuSupportsIpv6) {
doReturn(IPV6_MIN_MTU - 1).when(mTestDeps).calculateVpnMtu(any(), anyInt(), anyInt(),
@@ -1931,7 +1937,7 @@
.thenReturn(vpnProfile.encode());
vpn.startVpnProfile(TEST_VPN_PKG);
- final NetworkCallback nwCb = triggerOnAvailableAndGetCallback();
+ final NetworkCallback nwCb = triggerOnAvailableAndGetCallback(underlyingNetworkCaps);
verify(mExecutor, atLeastOnce()).schedule(any(Runnable.class), anyLong(), any());
reset(mExecutor);
@@ -2085,15 +2091,16 @@
doTestMigrateIkeSession(ikeProfile.toVpnProfile(),
expectedKeepalive,
ESP_IP_VERSION_AUTO /* expectedIpVersion */,
- ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ new NetworkCapabilities.Builder().build());
}
- private void doTestMigrateIkeSession_FromIkeTunnConnParams(
+ private Ikev2VpnProfile makeIkeV2VpnProfile(
boolean isAutomaticIpVersionSelectionEnabled,
boolean isAutomaticNattKeepaliveTimerEnabled,
int keepaliveInProfile,
int ipVersionInProfile,
- int encapTypeInProfile) throws Exception {
+ int encapTypeInProfile) {
// TODO: Update helper function in IkeSessionTestUtils to support building IkeSessionParams
// with IP version and encap type when mainline-prod branch support these two APIs.
final IkeSessionParams params = getTestIkeSessionParams(true /* testIpv6 */,
@@ -2105,12 +2112,40 @@
final IkeTunnelConnectionParams tunnelParams =
new IkeTunnelConnectionParams(ikeSessionParams, CHILD_PARAMS);
- final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams)
+ return new Ikev2VpnProfile.Builder(tunnelParams)
.setBypassable(true)
.setAutomaticNattKeepaliveTimerEnabled(isAutomaticNattKeepaliveTimerEnabled)
.setAutomaticIpVersionSelectionEnabled(isAutomaticIpVersionSelectionEnabled)
.build();
+ }
+ private void doTestMigrateIkeSession_FromIkeTunnConnParams(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile) throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(isAutomaticIpVersionSelectionEnabled,
+ isAutomaticNattKeepaliveTimerEnabled, keepaliveInProfile, ipVersionInProfile,
+ encapTypeInProfile, new NetworkCapabilities.Builder().build());
+ }
+
+ private void doTestMigrateIkeSession_FromIkeTunnConnParams(
+ boolean isAutomaticIpVersionSelectionEnabled,
+ boolean isAutomaticNattKeepaliveTimerEnabled,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile,
+ @NonNull final NetworkCapabilities nc) throws Exception {
+ final Ikev2VpnProfile ikeProfile = makeIkeV2VpnProfile(
+ isAutomaticIpVersionSelectionEnabled,
+ isAutomaticNattKeepaliveTimerEnabled,
+ keepaliveInProfile,
+ ipVersionInProfile,
+ encapTypeInProfile);
+
+ final IkeSessionParams ikeSessionParams =
+ ikeProfile.getIkeTunnelConnectionParams().getIkeSessionParams();
final int expectedKeepalive = isAutomaticNattKeepaliveTimerEnabled
? AUTOMATIC_KEEPALIVE_DELAY_SECONDS
: ikeSessionParams.getNattKeepAliveDelaySeconds();
@@ -2121,22 +2156,48 @@
? ESP_ENCAP_TYPE_AUTO
: ikeSessionParams.getEncapType();
doTestMigrateIkeSession(ikeProfile.toVpnProfile(), expectedKeepalive,
- expectedIpVersion, expectedEncapType);
+ expectedIpVersion, expectedEncapType, nc);
}
- private void doTestMigrateIkeSession(VpnProfile profile,
- int expectedKeepalive, int expectedIpVersion, int expectedEncapType) throws Exception {
+ @Test
+ public void doTestMigrateIkeSession_Vcn() throws Exception {
+ final int expectedKeepalive = 2097; // Any unlikely number will do
+ final NetworkCapabilities vcnNc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setTransportInfo(new VcnTransportInfo(TEST_SUB_ID, expectedKeepalive))
+ .build();
+ final Ikev2VpnProfile ikev2VpnProfile = makeIkeV2VpnProfile(
+ true /* isAutomaticIpVersionSelectionEnabled */,
+ true /* isAutomaticNattKeepaliveTimerEnabled */,
+ 234 /* keepaliveInProfile */, // Should be ignored, any value will do
+ ESP_IP_VERSION_IPV4, // Should be ignored
+ ESP_ENCAP_TYPE_UDP // Should be ignored
+ );
+ doTestMigrateIkeSession(
+ ikev2VpnProfile.toVpnProfile(),
+ expectedKeepalive,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */,
+ vcnNc);
+ }
+
+ private void doTestMigrateIkeSession(
+ @NonNull final VpnProfile profile,
+ final int expectedKeepalive,
+ final int expectedIpVersion,
+ final int expectedEncapType,
+ @NonNull final NetworkCapabilities caps) throws Exception {
final PlatformVpnSnapshot vpnSnapShot =
verifySetupPlatformVpn(profile,
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ caps /* underlying network capabilities */,
false /* mtuSupportsIpv6 */,
expectedKeepalive < DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC);
// Simulate a new network coming up
vpnSnapShot.nwCb.onAvailable(TEST_NETWORK_2);
verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
- vpnSnapShot.nwCb.onCapabilitiesChanged(
- TEST_NETWORK_2, new NetworkCapabilities.Builder().build());
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2, caps);
// Verify MOBIKE is triggered
verify(mIkeSessionWrapper, timeout(TEST_TIMEOUT_MS)).setNetwork(TEST_NETWORK_2,
expectedIpVersion, expectedEncapType, expectedKeepalive);
@@ -2162,6 +2223,7 @@
final PlatformVpnSnapshot vpnSnapShot =
verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
hasV6 /* mtuSupportsIpv6 */,
false /* areLongLivedTcpConnectionsExpensive */);
reset(mExecutor);
@@ -2349,6 +2411,7 @@
final PlatformVpnSnapshot vpnSnapShot =
verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ new NetworkCapabilities.Builder().build() /* underlying network caps */,
false /* mtuSupportsIpv6 */,
true /* areLongLivedTcpConnectionsExpensive */);
@@ -2548,6 +2611,81 @@
vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
}
+ private String getDump(@NonNull final Vpn vpn) {
+ final StringWriter sw = new StringWriter();
+ final IndentingPrintWriter writer = new IndentingPrintWriter(sw, "");
+ vpn.dump(writer);
+ writer.flush();
+ return sw.toString();
+ }
+
+ private int countMatches(@NonNull final Pattern regexp, @NonNull final String string) {
+ final Matcher m = regexp.matcher(string);
+ int i = 0;
+ while (m.find()) ++i;
+ return i;
+ }
+
+ @Test
+ public void testNCEventChanges() throws Exception {
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .setLinkDownstreamBandwidthKbps(1000)
+ .setLinkUpstreamBandwidthKbps(500);
+
+ final Ikev2VpnProfile ikeProfile =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY)
+ .setAuthPsk(TEST_VPN_PSK)
+ .setBypassable(true /* isBypassable */)
+ .setAutomaticNattKeepaliveTimerEnabled(true)
+ .setAutomaticIpVersionSelectionEnabled(true)
+ .build();
+
+ final PlatformVpnSnapshot vpnSnapShot =
+ verifySetupPlatformVpn(ikeProfile.toVpnProfile(),
+ createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
+ ncBuilder.build(), false /* mtuSupportsIpv6 */,
+ true /* areLongLivedTcpConnectionsExpensive */);
+
+ // Calls to onCapabilitiesChanged will be thrown to the executor for execution ; by
+ // default this will incur a 10ms delay before it's executed, messing with the timing
+ // of the log and having the checks for counts in equals() below flake.
+ mExecutor.executeDirect = true;
+
+ // First nc changed triggered by verifySetupPlatformVpn
+ final Pattern pattern = Pattern.compile("Cap changed from", Pattern.MULTILINE);
+ final String stage1 = getDump(vpnSnapShot.vpn);
+ assertEquals(1, countMatches(pattern, stage1));
+
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage2 = getDump(vpnSnapShot.vpn);
+ // Was the same caps, there should still be only 1 match
+ assertEquals(1, countMatches(pattern, stage2));
+
+ ncBuilder.setLinkDownstreamBandwidthKbps(1200)
+ .setLinkUpstreamBandwidthKbps(300);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage3 = getDump(vpnSnapShot.vpn);
+ // Was not an important change, should not be logged, still only 1 match
+ assertEquals(1, countMatches(pattern, stage3));
+
+ ncBuilder.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage4 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match
+ assertEquals(2, countMatches(pattern, stage4));
+
+ ncBuilder.removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ ncBuilder.setLinkDownstreamBandwidthKbps(600);
+ vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK, ncBuilder.build());
+ final String stage5 = getDump(vpnSnapShot.vpn);
+ // Change to caps is important, should cause a new match even with the unimportant change
+ assertEquals(3, countMatches(pattern, stage5));
+ }
+ // TODO : beef up event logs tests
+
private void verifyHandlingNetworkLoss(PlatformVpnSnapshot vpnSnapShot) throws Exception {
// Forget the #sendLinkProperties during first setup.
reset(mMockNetworkAgent);
@@ -3045,30 +3183,4 @@
} catch (Exception e) {
}
}
-
- private void setMockedNetworks(final Map<Network, NetworkCapabilities> networks) {
- doAnswer(invocation -> {
- final Network network = (Network) invocation.getArguments()[0];
- return networks.get(network);
- }).when(mConnectivityManager).getNetworkCapabilities(any());
- }
-
- // Need multiple copies of this, but Java's Stream objects can't be reused or
- // duplicated.
- private Stream<String> publicIpV4Routes() {
- return Stream.of(
- "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4",
- "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6",
- "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9",
- "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11",
- "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14",
- "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7",
- "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4");
- }
-
- private Stream<String> publicIpV6Routes() {
- return Stream.of(
- "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6",
- "fe00::/8", "2605:ef80:e:af1d::/64");
- }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index a917361..d9acc61 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -23,11 +23,13 @@
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
+import com.android.net.module.util.SharedLog
import com.android.server.connectivity.mdns.MdnsAdvertiser.AdvertiserCallback
import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketCallback
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.waitForIdle
+import java.net.NetworkInterface
import java.util.Objects
import org.junit.After
import org.junit.Before
@@ -47,25 +49,50 @@
private const val SERVICE_ID_1 = 1
private const val SERVICE_ID_2 = 2
+private const val LONG_SERVICE_ID_1 = 3
+private const val LONG_SERVICE_ID_2 = 4
+private const val CASE_INSENSITIVE_TEST_SERVICE_ID = 5
private const val TIMEOUT_MS = 10_000L
private val TEST_ADDR = parseNumericAddress("2001:db8::123")
private val TEST_LINKADDR = LinkAddress(TEST_ADDR, 64 /* prefixLength */)
private val TEST_NETWORK_1 = mock(Network::class.java)
private val TEST_NETWORK_2 = mock(Network::class.java)
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
+private const val TEST_SUBTYPE = "_subtype"
private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
port = 12345
- host = TEST_ADDR
+ hostAddresses = listOf(TEST_ADDR)
network = TEST_NETWORK_1
}
+private val LONG_SERVICE_1 =
+ NsdServiceInfo("a".repeat(48) + "TestServiceName", "_longadvertisertest._tcp").apply {
+ port = 12345
+ hostAddresses = listOf(TEST_ADDR)
+ network = TEST_NETWORK_1
+ }
+
private val ALL_NETWORKS_SERVICE = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply {
port = 12345
- host = TEST_ADDR
+ hostAddresses = listOf(TEST_ADDR)
network = null
}
+private val ALL_NETWORKS_SERVICE_2 =
+ NsdServiceInfo("TESTSERVICENAME", "_ADVERTISERTEST._tcp").apply {
+ port = 12345
+ hostAddresses = listOf(TEST_ADDR)
+ network = null
+ }
+
+private val LONG_ALL_NETWORKS_SERVICE =
+ NsdServiceInfo("a".repeat(48) + "TestServiceName", "_longadvertisertest._tcp").apply {
+ port = 12345
+ hostAddresses = listOf(TEST_ADDR)
+ network = null
+ }
+
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsAdvertiserTest {
@@ -73,6 +100,7 @@
private val handler by lazy { Handler(thread.looper) }
private val socketProvider = mock(MdnsSocketProvider::class.java)
private val cb = mock(AdvertiserCallback::class.java)
+ private val sharedlog = mock(SharedLog::class.java)
private val mockSocket1 = mock(MdnsInterfaceSocket::class.java)
private val mockSocket2 = mock(MdnsInterfaceSocket::class.java)
@@ -85,13 +113,15 @@
thread.start()
doReturn(TEST_HOSTNAME).`when`(mockDeps).generateHostname()
doReturn(mockInterfaceAdvertiser1).`when`(mockDeps).makeAdvertiser(eq(mockSocket1),
- any(), any(), any(), any(), eq(TEST_HOSTNAME)
+ any(), any(), any(), any(), eq(TEST_HOSTNAME), any()
)
doReturn(mockInterfaceAdvertiser2).`when`(mockDeps).makeAdvertiser(eq(mockSocket2),
- any(), any(), any(), any(), eq(TEST_HOSTNAME)
+ any(), any(), any(), any(), eq(TEST_HOSTNAME), any()
)
doReturn(true).`when`(mockInterfaceAdvertiser1).isProbing(anyInt())
doReturn(true).`when`(mockInterfaceAdvertiser2).isProbing(anyInt())
+ doReturn(createEmptyNetworkInterface()).`when`(mockSocket1).getInterface()
+ doReturn(createEmptyNetworkInterface()).`when`(mockSocket2).getInterface()
}
@After
@@ -100,10 +130,16 @@
thread.join()
}
+ private fun createEmptyNetworkInterface(): NetworkInterface {
+ val constructor = NetworkInterface::class.java.getDeclaredConstructor()
+ constructor.isAccessible = true
+ return constructor.newInstance()
+ }
+
@Test
fun testAddService_OneNetwork() {
- val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps)
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1) }
+ val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog)
+ postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), socketCbCaptor.capture())
@@ -118,7 +154,8 @@
eq(thread.looper),
any(),
intAdvCbCaptor.capture(),
- eq(TEST_HOSTNAME)
+ eq(TEST_HOSTNAME),
+ any()
)
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
@@ -132,8 +169,8 @@
@Test
fun testAddService_AllNetworks() {
- val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps)
- postSync { advertiser.addService(SERVICE_ID_1, ALL_NETWORKS_SERVICE) }
+ val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog)
+ postSync { advertiser.addService(SERVICE_ID_1, ALL_NETWORKS_SERVICE, TEST_SUBTYPE) }
val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(ALL_NETWORKS_SERVICE.network),
@@ -146,11 +183,15 @@
val intAdvCbCaptor1 = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java)
val intAdvCbCaptor2 = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java)
verify(mockDeps).makeAdvertiser(eq(mockSocket1), eq(listOf(TEST_LINKADDR)),
- eq(thread.looper), any(), intAdvCbCaptor1.capture(), eq(TEST_HOSTNAME)
+ eq(thread.looper), any(), intAdvCbCaptor1.capture(), eq(TEST_HOSTNAME), any()
)
verify(mockDeps).makeAdvertiser(eq(mockSocket2), eq(listOf(TEST_LINKADDR)),
- eq(thread.looper), any(), intAdvCbCaptor2.capture(), eq(TEST_HOSTNAME)
+ eq(thread.looper), any(), intAdvCbCaptor2.capture(), eq(TEST_HOSTNAME), any()
)
+ verify(mockInterfaceAdvertiser1).addService(
+ anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
+ verify(mockInterfaceAdvertiser2).addService(
+ anyInt(), eq(ALL_NETWORKS_SERVICE), eq(TEST_SUBTYPE))
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
postSync { intAdvCbCaptor1.value.onRegisterServiceSucceeded(
@@ -178,19 +219,26 @@
@Test
fun testAddService_Conflicts() {
- val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps)
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1) }
+ val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog)
+ postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
val oneNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(TEST_NETWORK_1), oneNetSocketCbCaptor.capture())
val oneNetSocketCb = oneNetSocketCbCaptor.value
// Register a service with the same name on all networks (name conflict)
- postSync { advertiser.addService(SERVICE_ID_2, ALL_NETWORKS_SERVICE) }
+ postSync { advertiser.addService(SERVICE_ID_2, ALL_NETWORKS_SERVICE, null /* subtype */) }
val allNetSocketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java)
verify(socketProvider).requestSocket(eq(null), allNetSocketCbCaptor.capture())
val allNetSocketCb = allNetSocketCbCaptor.value
+ postSync { advertiser.addService(LONG_SERVICE_ID_1, LONG_SERVICE_1, null /* subtype */) }
+ postSync { advertiser.addService(LONG_SERVICE_ID_2, LONG_ALL_NETWORKS_SERVICE,
+ null /* subtype */) }
+
+ postSync { advertiser.addService(CASE_INSENSITIVE_TEST_SERVICE_ID, ALL_NETWORKS_SERVICE_2,
+ null /* subtype */) }
+
// Callbacks for matching network and all networks both get the socket
postSync {
oneNetSocketCb.onSocketCreated(TEST_NETWORK_1, mockSocket1, listOf(TEST_LINKADDR))
@@ -200,18 +248,40 @@
val expectedRenamed = NsdServiceInfo(
"${ALL_NETWORKS_SERVICE.serviceName} (2)", ALL_NETWORKS_SERVICE.serviceType).apply {
port = ALL_NETWORKS_SERVICE.port
- host = ALL_NETWORKS_SERVICE.host
+ hostAddresses = ALL_NETWORKS_SERVICE.hostAddresses
network = ALL_NETWORKS_SERVICE.network
}
+ val expectedLongRenamed = NsdServiceInfo(
+ "${LONG_ALL_NETWORKS_SERVICE.serviceName.dropLast(4)} (2)",
+ LONG_ALL_NETWORKS_SERVICE.serviceType).apply {
+ port = LONG_ALL_NETWORKS_SERVICE.port
+ hostAddresses = LONG_ALL_NETWORKS_SERVICE.hostAddresses
+ network = LONG_ALL_NETWORKS_SERVICE.network
+ }
+
+ val expectedCaseInsensitiveRenamed = NsdServiceInfo(
+ "${ALL_NETWORKS_SERVICE_2.serviceName} (3)", ALL_NETWORKS_SERVICE_2.serviceType
+ ).apply {
+ port = ALL_NETWORKS_SERVICE_2.port
+ hostAddresses = ALL_NETWORKS_SERVICE_2.hostAddresses
+ network = ALL_NETWORKS_SERVICE_2.network
+ }
+
val intAdvCbCaptor = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java)
verify(mockDeps).makeAdvertiser(eq(mockSocket1), eq(listOf(TEST_LINKADDR)),
- eq(thread.looper), any(), intAdvCbCaptor.capture(), eq(TEST_HOSTNAME)
+ eq(thread.looper), any(), intAdvCbCaptor.capture(), eq(TEST_HOSTNAME), any()
)
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_1),
- argThat { it.matches(SERVICE_1) })
+ argThat { it.matches(SERVICE_1) }, eq(null))
verify(mockInterfaceAdvertiser1).addService(eq(SERVICE_ID_2),
- argThat { it.matches(expectedRenamed) })
+ argThat { it.matches(expectedRenamed) }, eq(null))
+ verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_1),
+ argThat { it.matches(LONG_SERVICE_1) }, eq(null))
+ verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_2),
+ argThat { it.matches(expectedLongRenamed) }, eq(null))
+ verify(mockInterfaceAdvertiser1).addService(eq(CASE_INSENSITIVE_TEST_SERVICE_ID),
+ argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
@@ -233,9 +303,9 @@
@Test
fun testRemoveService_whenAllServiceRemoved_thenUpdateHostName() {
- val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps)
+ val advertiser = MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog)
verify(mockDeps, times(1)).generateHostname()
- postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1) }
+ postSync { advertiser.addService(SERVICE_ID_1, SERVICE_1, null /* subtype */) }
postSync { advertiser.removeService(SERVICE_ID_1) }
verify(mockDeps, times(2)).generateHostname()
}
@@ -251,7 +321,7 @@
return Objects.equals(serviceName, other.serviceName) &&
Objects.equals(serviceType, other.serviceType) &&
Objects.equals(attributes, other.attributes) &&
- Objects.equals(host, other.host) &&
+ Objects.equals(hostAddresses, other.hostAddresses) &&
port == other.port &&
Objects.equals(network, other.network)
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
index 7e7e6a4..a54a521 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -18,26 +18,36 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
-import static org.mockito.Mockito.any;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Pair;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.io.IOException;
@@ -50,98 +60,238 @@
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
public class MdnsDiscoveryManagerTests {
-
+ private static final long DEFAULT_TIMEOUT = 2000L;
private static final String SERVICE_TYPE_1 = "_googlecast._tcp.local";
private static final String SERVICE_TYPE_2 = "_test._tcp.local";
- private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_1 =
+ private static final Network NETWORK_1 = Mockito.mock(Network.class);
+ private static final Network NETWORK_2 = Mockito.mock(Network.class);
+ private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_1_NULL_NETWORK =
Pair.create(SERVICE_TYPE_1, null);
- private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2 =
+ private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_1_NETWORK_1 =
+ Pair.create(SERVICE_TYPE_1, NETWORK_1);
+ private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NULL_NETWORK =
Pair.create(SERVICE_TYPE_2, null);
+ private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NETWORK_1 =
+ Pair.create(SERVICE_TYPE_2, NETWORK_1);
+ private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NETWORK_2 =
+ Pair.create(SERVICE_TYPE_2, NETWORK_2);
@Mock private ExecutorProvider executorProvider;
@Mock private MdnsSocketClientBase socketClient;
- @Mock private MdnsServiceTypeClient mockServiceTypeClientOne;
- @Mock private MdnsServiceTypeClient mockServiceTypeClientTwo;
+ @Mock private MdnsServiceTypeClient mockServiceTypeClientType1NullNetwork;
+ @Mock private MdnsServiceTypeClient mockServiceTypeClientType1Network1;
+ @Mock private MdnsServiceTypeClient mockServiceTypeClientType2NullNetwork;
+ @Mock private MdnsServiceTypeClient mockServiceTypeClientType2Network1;
+ @Mock private MdnsServiceTypeClient mockServiceTypeClientType2Network2;
@Mock MdnsServiceBrowserListener mockListenerOne;
@Mock MdnsServiceBrowserListener mockListenerTwo;
+ @Mock SharedLog sharedLog;
private MdnsDiscoveryManager discoveryManager;
+ private HandlerThread thread;
+ private Handler handler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mockServiceTypeClientOne.getServiceTypeLabels())
- .thenReturn(TextUtils.split(SERVICE_TYPE_1, "\\."));
- when(mockServiceTypeClientTwo.getServiceTypeLabels())
- .thenReturn(TextUtils.split(SERVICE_TYPE_2, "\\."));
-
- discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient) {
+ thread = new HandlerThread("MdnsDiscoveryManagerTests");
+ thread.start();
+ handler = new Handler(thread.getLooper());
+ doReturn(thread.getLooper()).when(socketClient).getLooper();
+ doReturn(true).when(socketClient).supportsRequestingSpecificNetworks();
+ discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient,
+ sharedLog) {
@Override
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@Nullable Network network) {
final Pair<String, Network> perNetworkServiceType =
Pair.create(serviceType, network);
- if (perNetworkServiceType.equals(PER_NETWORK_SERVICE_TYPE_1)) {
- return mockServiceTypeClientOne;
- } else if (perNetworkServiceType.equals(PER_NETWORK_SERVICE_TYPE_2)) {
- return mockServiceTypeClientTwo;
+ if (perNetworkServiceType.equals(PER_NETWORK_SERVICE_TYPE_1_NULL_NETWORK)) {
+ return mockServiceTypeClientType1NullNetwork;
+ } else if (perNetworkServiceType.equals(
+ PER_NETWORK_SERVICE_TYPE_1_NETWORK_1)) {
+ return mockServiceTypeClientType1Network1;
+ } else if (perNetworkServiceType.equals(
+ PER_NETWORK_SERVICE_TYPE_2_NULL_NETWORK)) {
+ return mockServiceTypeClientType2NullNetwork;
+ } else if (perNetworkServiceType.equals(
+ PER_NETWORK_SERVICE_TYPE_2_NETWORK_1)) {
+ return mockServiceTypeClientType2Network1;
+ } else if (perNetworkServiceType.equals(
+ PER_NETWORK_SERVICE_TYPE_2_NETWORK_2)) {
+ return mockServiceTypeClientType2Network2;
}
return null;
}
};
}
- private void verifyListenerRegistration(String serviceType, MdnsServiceBrowserListener listener,
- MdnsServiceTypeClient client) throws IOException {
+ @After
+ public void tearDown() {
+ if (thread != null) {
+ thread.quitSafely();
+ }
+ }
+
+ private void runOnHandler(Runnable r) {
+ handler.post(r);
+ HandlerUtils.waitForIdle(handler, DEFAULT_TIMEOUT);
+ }
+
+ private SocketCreationCallback expectSocketCreationCallback(String serviceType,
+ MdnsServiceBrowserListener listener, MdnsSearchOptions options) throws IOException {
final ArgumentCaptor<SocketCreationCallback> callbackCaptor =
ArgumentCaptor.forClass(SocketCreationCallback.class);
- discoveryManager.registerListener(serviceType, listener,
- MdnsSearchOptions.getDefaultOptions());
+ runOnHandler(() -> discoveryManager.registerListener(serviceType, listener, options));
verify(socketClient).startDiscovery();
verify(socketClient).notifyNetworkRequested(
- eq(listener), any(), callbackCaptor.capture());
- final SocketCreationCallback callback = callbackCaptor.getValue();
- callback.onSocketCreated(null /* network */);
- verify(client).startSendAndReceive(listener, MdnsSearchOptions.getDefaultOptions());
+ eq(listener), eq(options.getNetwork()), callbackCaptor.capture());
+ return callbackCaptor.getValue();
}
@Test
public void registerListener_unregisterListener() throws IOException {
- verifyListenerRegistration(SERVICE_TYPE_1, mockListenerOne, mockServiceTypeClientOne);
+ final MdnsSearchOptions options =
+ MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, options);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
+ verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options);
- when(mockServiceTypeClientOne.stopSendAndReceive(mockListenerOne)).thenReturn(true);
- discoveryManager.unregisterListener(SERVICE_TYPE_1, mockListenerOne);
- verify(mockServiceTypeClientOne).stopSendAndReceive(mockListenerOne);
+ when(mockServiceTypeClientType1NullNetwork.stopSendAndReceive(mockListenerOne))
+ .thenReturn(true);
+ runOnHandler(() -> discoveryManager.unregisterListener(SERVICE_TYPE_1, mockListenerOne));
+ verify(mockServiceTypeClientType1NullNetwork).stopSendAndReceive(mockListenerOne);
verify(socketClient).stopDiscovery();
}
@Test
public void registerMultipleListeners() throws IOException {
- verifyListenerRegistration(SERVICE_TYPE_1, mockListenerOne, mockServiceTypeClientOne);
- verifyListenerRegistration(SERVICE_TYPE_2, mockListenerTwo, mockServiceTypeClientTwo);
+ final MdnsSearchOptions options =
+ MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, options);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
+ verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options);
+
+ final SocketCreationCallback callback2 = expectSocketCreationCallback(
+ SERVICE_TYPE_2, mockListenerTwo, options);
+ runOnHandler(() -> callback2.onSocketCreated(null /* network */));
+ verify(mockServiceTypeClientType2NullNetwork).startSendAndReceive(mockListenerTwo, options);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_2));
+ verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options);
}
@Test
public void onResponseReceived() throws IOException {
- verifyListenerRegistration(SERVICE_TYPE_1, mockListenerOne, mockServiceTypeClientOne);
- verifyListenerRegistration(SERVICE_TYPE_2, mockListenerTwo, mockServiceTypeClientTwo);
+ final MdnsSearchOptions options1 =
+ MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, options1);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
+ verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(
+ mockListenerOne, options1);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options1);
- MdnsPacket responseForServiceTypeOne = createMdnsPacket(SERVICE_TYPE_1);
+ final MdnsSearchOptions options2 =
+ MdnsSearchOptions.newBuilder().setNetwork(NETWORK_2).build();
+ final SocketCreationCallback callback2 = expectSocketCreationCallback(
+ SERVICE_TYPE_2, mockListenerTwo, options2);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_2));
+ verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options2);
+
+ final MdnsPacket responseForServiceTypeOne = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1;
- discoveryManager.onResponseReceived(responseForServiceTypeOne, ifIndex, null /* network */);
- verify(mockServiceTypeClientOne).processResponse(responseForServiceTypeOne, ifIndex,
- null /* network */);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForServiceTypeOne, ifIndex, null /* network */));
+ // Packets for network null are only processed by the ServiceTypeClient for network null
+ verify(mockServiceTypeClientType1NullNetwork).processResponse(responseForServiceTypeOne,
+ ifIndex, null /* network */);
+ verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(), any());
+ verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(), any());
- MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2);
- discoveryManager.onResponseReceived(responseForServiceTypeTwo, ifIndex, null /* network */);
- verify(mockServiceTypeClientTwo).processResponse(responseForServiceTypeTwo, ifIndex,
- null /* network */);
+ final MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForServiceTypeTwo, ifIndex, NETWORK_1));
+ verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(),
+ eq(NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).processResponse(responseForServiceTypeTwo,
+ ifIndex, NETWORK_1);
+ verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(),
+ eq(NETWORK_1));
- MdnsPacket responseForSubtype = createMdnsPacket("subtype._sub._googlecast._tcp.local");
- discoveryManager.onResponseReceived(responseForSubtype, ifIndex, null /* network */);
- verify(mockServiceTypeClientOne).processResponse(responseForSubtype, ifIndex,
- null /* network */);
+ final MdnsPacket responseForSubtype =
+ createMdnsPacket("subtype._sub._googlecast._tcp.local");
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForSubtype, ifIndex, NETWORK_2));
+ verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(
+ any(), anyInt(), eq(NETWORK_2));
+ verify(mockServiceTypeClientType1Network1, never()).processResponse(
+ any(), anyInt(), eq(NETWORK_2));
+ verify(mockServiceTypeClientType2Network2).processResponse(
+ responseForSubtype, ifIndex, NETWORK_2);
+ }
+
+ @Test
+ public void testSocketCreatedAndDestroyed() throws IOException {
+ // Create a ServiceTypeClient for SERVICE_TYPE_1 and NETWORK_1
+ final MdnsSearchOptions network1Options =
+ MdnsSearchOptions.newBuilder().setNetwork(NETWORK_1).build();
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, network1Options);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).startSendAndReceive(
+ mockListenerOne, network1Options);
+
+ // Create a ServiceTypeClient for SERVICE_TYPE_2 and NETWORK_1
+ final SocketCreationCallback callback2 = expectSocketCreationCallback(
+ SERVICE_TYPE_2, mockListenerTwo, network1Options);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_1));
+ verify(mockServiceTypeClientType2Network1).startSendAndReceive(
+ mockListenerTwo, network1Options);
+
+ // Receive a response, it should be processed on both clients.
+ final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
+ final int ifIndex = 1;
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).processResponse(response, ifIndex, NETWORK_1);
+ verify(mockServiceTypeClientType2Network1).processResponse(response, ifIndex, NETWORK_1);
+
+ // The first callback receives a notification that the network has been destroyed,
+ // mockServiceTypeClientOne1 should send service removed notifications and remove from the
+ // list of clients.
+ runOnHandler(() -> callback.onAllSocketsDestroyed(NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).notifySocketDestroyed();
+
+ // Receive a response again, it should be processed only on
+ // mockServiceTypeClientType2Network1. Because the mockServiceTypeClientType1Network1 is
+ // removed from the list of clients, it is no longer able to process responses.
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, NETWORK_1));
+ // Still times(1) as a response was received once previously
+ verify(mockServiceTypeClientType1Network1, times(1))
+ .processResponse(response, ifIndex, NETWORK_1);
+ verify(mockServiceTypeClientType2Network1, times(2))
+ .processResponse(response, ifIndex, NETWORK_1);
+
+ // The client for NETWORK_1 receives the callback that the NETWORK_2 has been destroyed,
+ // mockServiceTypeClientTwo2 shouldn't send any notifications.
+ runOnHandler(() -> callback2.onAllSocketsDestroyed(NETWORK_2));
+ verify(mockServiceTypeClientType2Network1, never()).notifySocketDestroyed();
+
+ // Receive a response again, mockServiceTypeClientType2Network1 is still in the list of
+ // clients, it's still able to process responses.
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, NETWORK_1));
+ verify(mockServiceTypeClientType1Network1, times(1))
+ .processResponse(response, ifIndex, NETWORK_1);
+ verify(mockServiceTypeClientType2Network1, times(3))
+ .processResponse(response, ifIndex, NETWORK_1);
}
private MdnsPacket createMdnsPacket(String serviceType) {
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
index 0ca0835..dd458b8 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -22,6 +22,7 @@
import android.os.Build
import android.os.HandlerThread
import com.android.net.module.util.HexDump
+import com.android.net.module.util.SharedLog
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo
import com.android.server.connectivity.mdns.MdnsAnnouncer.ExitAnnouncementInfo
@@ -75,6 +76,7 @@
private val replySender = mock(MdnsReplySender::class.java)
private val announcer = mock(MdnsAnnouncer::class.java)
private val prober = mock(MdnsProber::class.java)
+ private val sharedlog = SharedLog("MdnsInterfaceAdvertiserTest")
@Suppress("UNCHECKED_CAST")
private val probeCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
as ArgumentCaptor<PacketRepeaterCallback<ProbingInfo>>
@@ -90,14 +92,14 @@
private val advertiser by lazy {
MdnsInterfaceAdvertiser(
- LOG_TAG,
socket,
TEST_ADDRS,
thread.looper,
TEST_BUFFER,
cb,
deps,
- TEST_HOSTNAME
+ TEST_HOSTNAME,
+ sharedlog
)
}
@@ -113,8 +115,9 @@
val knownServices = mutableSetOf<Int>()
doAnswer { inv ->
knownServices.add(inv.getArgument(0))
+
-1
- }.`when`(repository).addService(anyInt(), any())
+ }.`when`(repository).addService(anyInt(), any(), any())
doAnswer { inv ->
knownServices.remove(inv.getArgument(0))
null
@@ -275,8 +278,8 @@
doReturn(serviceId).`when`(testProbingInfo).serviceId
doReturn(testProbingInfo).`when`(repository).setServiceProbing(serviceId)
- advertiser.addService(serviceId, serviceInfo)
- verify(repository).addService(serviceId, serviceInfo)
+ advertiser.addService(serviceId, serviceInfo, null /* subtype */)
+ verify(repository).addService(serviceId, serviceInfo, null /* subtype */)
verify(prober).startProbing(testProbingInfo)
// Simulate probing success: continues to announcing
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
index 90c43e5..87ba5d7 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -24,8 +24,12 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.net.InetAddresses;
import android.net.Network;
@@ -34,6 +38,7 @@
import android.os.HandlerThread;
import com.android.net.module.util.HexDump;
+import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -62,7 +67,7 @@
@Mock private MdnsInterfaceSocket mSocket;
@Mock private MdnsServiceBrowserListener mListener;
@Mock private MdnsSocketClientBase.Callback mCallback;
- @Mock private MdnsSocketClientBase.SocketCreationCallback mSocketCreationCallback;
+ @Mock private SocketCreationCallback mSocketCreationCallback;
private MdnsMultinetworkSocketClient mSocketClient;
private Handler mHandler;
@@ -77,12 +82,17 @@
}
private SocketCallback expectSocketCallback() {
+ return expectSocketCallback(mListener, mNetwork);
+ }
+
+ private SocketCallback expectSocketCallback(MdnsServiceBrowserListener listener,
+ Network requestedNetwork) {
final ArgumentCaptor<SocketCallback> callbackCaptor =
ArgumentCaptor.forClass(SocketCallback.class);
mHandler.post(() -> mSocketClient.notifyNetworkRequested(
- mListener, mNetwork, mSocketCreationCallback));
+ listener, requestedNetwork, mSocketCreationCallback));
verify(mProvider, timeout(DEFAULT_TIMEOUT))
- .requestSocket(eq(mNetwork), callbackCaptor.capture());
+ .requestSocket(eq(requestedNetwork), callbackCaptor.capture());
return callbackCaptor.getValue();
}
@@ -104,22 +114,36 @@
InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */);
- doReturn(true).when(mSocket).hasJoinedIpv4();
- doReturn(true).when(mSocket).hasJoinedIpv6();
- doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+
+ final MdnsInterfaceSocket tetherIfaceSock1 = mock(MdnsInterfaceSocket.class);
+ final MdnsInterfaceSocket tetherIfaceSock2 = mock(MdnsInterfaceSocket.class);
+ for (MdnsInterfaceSocket socket : List.of(mSocket, tetherIfaceSock1, tetherIfaceSock2)) {
+ doReturn(true).when(socket).hasJoinedIpv4();
+ doReturn(true).when(socket).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(socket).getInterface();
+ }
+
// Notify socket created
callback.onSocketCreated(mNetwork, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork);
+ callback.onSocketCreated(null, tetherIfaceSock1, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(null);
+ callback.onSocketCreated(null, tetherIfaceSock2, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
// Send packet to IPv4 with target network and verify sending has been called.
mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet);
+ verify(tetherIfaceSock1, never()).send(any());
+ verify(tetherIfaceSock2, never()).send(any());
// Send packet to IPv6 without target network and verify sending has been called.
- mSocketClient.sendMulticastPacket(ipv6Packet);
+ mSocketClient.sendMulticastPacket(ipv6Packet, null);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- verify(mSocket).send(ipv6Packet);
+ verify(mSocket, never()).send(ipv6Packet);
+ verify(tetherIfaceSock1).send(ipv6Packet);
+ verify(tetherIfaceSock2).send(ipv6Packet);
}
@Test
@@ -169,4 +193,128 @@
new String[] { "Android", "local" } /* serviceHost */)
), response.answers);
}
+
+ @Test
+ public void testSocketRemovedAfterNetworkUnrequested() throws IOException {
+ // Request sockets on all networks
+ final SocketCallback callback = expectSocketCallback(mListener, null);
+ final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
+
+ // Notify 3 socket created, including 2 tethered interfaces (null network)
+ final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class);
+ final MdnsInterfaceSocket socket3 = mock(MdnsInterfaceSocket.class);
+ doReturn(true).when(mSocket).hasJoinedIpv4();
+ doReturn(true).when(mSocket).hasJoinedIpv6();
+ doReturn(true).when(socket2).hasJoinedIpv4();
+ doReturn(true).when(socket2).hasJoinedIpv6();
+ doReturn(true).when(socket3).hasJoinedIpv4();
+ doReturn(true).when(socket3).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(socket2).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(socket3).getInterface();
+
+ callback.onSocketCreated(mNetwork, mSocket, List.of());
+ callback.onSocketCreated(null, socket2, List.of());
+ callback.onSocketCreated(null, socket3, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(mNetwork);
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
+
+ // Send IPv4 packet on the non-null Network and verify sending has been called.
+ mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket).send(ipv4Packet);
+ verify(socket2, never()).send(any());
+ verify(socket3, never()).send(any());
+
+ // Request another socket with null network, get the same interfaces
+ final SocketCreationCallback socketCreationCb2 = mock(SocketCreationCallback.class);
+ final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class);
+
+ // requestSocket is called a second time
+ final ArgumentCaptor<SocketCallback> callback2Captor =
+ ArgumentCaptor.forClass(SocketCallback.class);
+ mHandler.post(() -> mSocketClient.notifyNetworkRequested(
+ listener2, null, socketCreationCb2));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mProvider, times(2)).requestSocket(eq(null), callback2Captor.capture());
+ final SocketCallback callback2 = callback2Captor.getAllValues().get(1);
+
+ // Notify socket created for all networks.
+ callback2.onSocketCreated(mNetwork, mSocket, List.of());
+ callback2.onSocketCreated(null, socket2, List.of());
+ callback2.onSocketCreated(null, socket3, List.of());
+ verify(socketCreationCb2).onSocketCreated(mNetwork);
+ verify(socketCreationCb2, times(2)).onSocketCreated(null);
+
+ // Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets.
+ mSocketClient.sendMulticastPacket(ipv4Packet, null);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ // ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on
+ // mNetwork
+ verify(mSocket, times(1)).send(ipv4Packet);
+ verify(socket2).send(ipv4Packet);
+ verify(socket3).send(ipv4Packet);
+
+ // Unregister the second request
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2));
+ verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2);
+
+ // Send IPv4 packet again and verify it's still sent a second time
+ mSocketClient.sendMulticastPacket(ipv4Packet, null);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(socket2, times(2)).send(ipv4Packet);
+ verify(socket3, times(2)).send(ipv4Packet);
+
+ // Unrequest remaining sockets
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
+ verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback);
+
+ // Send IPv4 packet and verify no more sending.
+ mSocketClient.sendMulticastPacket(ipv4Packet, null);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket, times(1)).send(ipv4Packet);
+ verify(socket2, times(2)).send(ipv4Packet);
+ verify(socket3, times(2)).send(ipv4Packet);
+ }
+
+ @Test
+ public void testNotifyNetworkUnrequested_SocketsOnNullNetwork() {
+ final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketCallback callback = expectSocketCallback(
+ mListener, null /* requestedNetwork */);
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
+
+ callback.onSocketCreated(null /* network */, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(null);
+ callback.onSocketCreated(null /* network */, otherSocket, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
+
+ verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(null /* network */);
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+ verify(mProvider).unrequestSocket(callback);
+ verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */);
+ }
+
+ @Test
+ public void testSocketCreatedAndDestroyed_NullNetwork() throws IOException {
+ final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketCallback callback = expectSocketCallback(mListener, null /* network */);
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
+
+ callback.onSocketCreated(null /* network */, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(null);
+ callback.onSocketCreated(null /* network */, otherSocket, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
+
+ // Notify socket destroyed
+ callback.onInterfaceDestroyed(null /* network */, mSocket);
+ verifyNoMoreInteractions(mSocketCreationCallback);
+ callback.onInterfaceDestroyed(null /* network */, otherSocket);
+ verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
index 5c9c294..a545373 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
@@ -31,7 +31,7 @@
@Test
fun testNameCompression() {
val writer = MdnsPacketWriter(ByteArray(1000))
- writer.writeLabels(arrayOf("my", "first", "name"))
+ writer.writeLabels(arrayOf("my", "FIRST", "name"))
writer.writeLabels(arrayOf("my", "second", "name"))
writer.writeLabels(arrayOf("other", "first", "name"))
writer.writeLabels(arrayOf("my", "second", "name"))
@@ -41,7 +41,7 @@
InetSocketAddress(InetAddresses.parseNumericAddress("2001:db8::123"), 123))
// Each label takes length + 1. So "first.name" offset = 3, "name" offset = 9
- val expected = "my".label() + "first".label() + "name".label() + 0x00.toByte() +
+ val expected = "my".label() + "FIRST".label() + "name".label() + 0x00.toByte() +
// "my.second.name" offset = 15
"my".label() + "second".label() + byteArrayOf(0xC0.toByte(), 9) +
"other".label() + byteArrayOf(0xC0.toByte(), 3) +
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
index 2b5423b..0a8d78d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -48,6 +48,7 @@
private val TEST_SERVICE_NAME_1 = arrayOf("testservice", "_nmt", "_tcp", "local")
private val TEST_SERVICE_NAME_2 = arrayOf("testservice2", "_nmt", "_tcp", "local")
+private val TEST_SERVICE_NAME_3 = arrayOf("Testservice", "_nmt", "_tcp", "local")
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
@@ -129,6 +130,15 @@
}
@Test
+ fun testCreateProberCaseInsensitive() {
+ val probeInfo = TestProbeInfo(
+ listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890),
+ makeServiceRecord(TEST_SERVICE_NAME_2, 37890),
+ makeServiceRecord(TEST_SERVICE_NAME_3, 37890)))
+ assertEquals(2, probeInfo.getPacket(0).questions.size)
+ }
+
+ @Test
fun testProbeMultipleRecords() {
val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
val prober = TestProber(thread.looper, replySender, cb)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
index 44e0d08..0033b5a 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -43,7 +43,9 @@
private const val TEST_SERVICE_ID_1 = 42
private const val TEST_SERVICE_ID_2 = 43
+private const val TEST_SERVICE_ID_3 = 44
private const val TEST_PORT = 12345
+private const val TEST_SUBTYPE = "_subtype"
private val TEST_HOSTNAME = arrayOf("Android_000102030405060708090A0B0C0D0E0F", "local")
private val TEST_ADDRESSES = listOf(
LinkAddress(parseNumericAddress("192.0.2.111"), 24),
@@ -62,6 +64,12 @@
port = TEST_PORT
}
+private val TEST_SERVICE_3 = NsdServiceInfo().apply {
+ serviceType = "_TESTSERVICE._tcp"
+ serviceName = "MyTESTSERVICE"
+ port = TEST_PORT
+}
+
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsRecordRepositoryTest {
@@ -86,7 +94,8 @@
fun testAddServiceAndProbe() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
assertEquals(0, repository.servicesCount)
- assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1))
+ assertEquals(-1, repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
+ null /* subtype */))
assertEquals(1, repository.servicesCount)
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
@@ -118,18 +127,21 @@
@Test
fun testAddAndConflicts() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
assertFailsWith(NameConflictException::class) {
- repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */)
+ }
+ assertFailsWith(NameConflictException::class) {
+ repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3, null /* subtype */)
}
}
@Test
fun testInvalidReuseOfServiceId() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
assertFailsWith(IllegalArgumentException::class) {
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_2, null /* subtype */)
}
}
@@ -138,7 +150,7 @@
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
assertFalse(repository.hasActiveService(TEST_SERVICE_ID_1))
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
@@ -180,13 +192,49 @@
}
@Test
+ fun testExitAnnouncements_WithSubtype() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, TEST_SUBTYPE)
+ repository.onAdvertisementSent(TEST_SERVICE_ID_1)
+
+ val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
+ assertNotNull(exitAnnouncement)
+ assertEquals(1, repository.servicesCount)
+ val packet = exitAnnouncement.getPacket(0)
+
+ assertEquals(0x8400 /* response, authoritative */, packet.flags)
+ assertEquals(0, packet.questions.size)
+ assertEquals(0, packet.authorityRecords.size)
+ assertEquals(0, packet.additionalRecords.size)
+
+ assertContentEquals(listOf(
+ MdnsPointerRecord(
+ arrayOf("_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 0L /* ttlMillis */,
+ arrayOf("MyTestService", "_testservice", "_tcp", "local")),
+ MdnsPointerRecord(
+ arrayOf("_subtype", "_sub", "_testservice", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 0L /* ttlMillis */,
+ arrayOf("MyTestService", "_testservice", "_tcp", "local")),
+ ), packet.answers)
+
+ repository.removeService(TEST_SERVICE_ID_1)
+ assertEquals(0, repository.servicesCount)
+ }
+
+ @Test
fun testExitingServiceReAdded() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.exitService(TEST_SERVICE_ID_1)
- assertEquals(TEST_SERVICE_ID_1, repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1))
+ assertEquals(TEST_SERVICE_ID_1,
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */))
assertEquals(1, repository.servicesCount)
repository.removeService(TEST_SERVICE_ID_2)
@@ -196,7 +244,8 @@
@Test
fun testOnProbingSucceeded() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
- val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
+ TEST_SUBTYPE)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
val packet = announcementInfo.getPacket(0)
@@ -205,6 +254,7 @@
assertEquals(0, packet.authorityRecords.size)
val serviceType = arrayOf("_testservice", "_tcp", "local")
+ val serviceSubtype = arrayOf(TEST_SUBTYPE, "_sub", "_testservice", "_tcp", "local")
val serviceName = arrayOf("MyTestService", "_testservice", "_tcp", "local")
val v4AddrRev = getReverseDnsAddress(TEST_ADDRESSES[0].address)
val v6Addr1Rev = getReverseDnsAddress(TEST_ADDRESSES[1].address)
@@ -250,6 +300,13 @@
false /* cacheFlush */,
4500000L /* ttlMillis */,
serviceName),
+ MdnsPointerRecord(
+ serviceSubtype,
+ 0L /* receiptTimeMillis */,
+ // Not a unique name owned by the announcer, so cacheFlush=false
+ false /* cacheFlush */,
+ 4500000L /* ttlMillis */,
+ serviceName),
MdnsServiceRecord(
serviceName,
0L /* receiptTimeMillis */,
@@ -318,10 +375,43 @@
}
@Test
- fun testGetReply() {
+ fun testGetReplyCaseInsensitive() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- val questions = listOf(MdnsPointerRecord(arrayOf("_testservice", "_tcp", "local"),
+ val questionsCaseInSensitive =
+ listOf(MdnsPointerRecord(arrayOf("_TESTSERVICE", "_TCP", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ // TTL and data is empty for a question
+ 0L /* ttlMillis */,
+ null /* pointer */))
+ val queryCaseInsensitive = MdnsPacket(0 /* flags */, questionsCaseInSensitive,
+ listOf() /* answers */, listOf() /* authorityRecords */,
+ listOf() /* additionalRecords */)
+ val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val replyCaseInsensitive = repository.getReply(queryCaseInsensitive, src)
+ assertNotNull(replyCaseInsensitive)
+ assertEquals(1, replyCaseInsensitive.answers.size)
+ assertEquals(7, replyCaseInsensitive.additionalAnswers.size)
+ }
+
+ @Test
+ fun testGetReply() {
+ doGetReplyTest(subtype = null)
+ }
+
+ @Test
+ fun testGetReply_WithSubtype() {
+ doGetReplyTest(TEST_SUBTYPE)
+ }
+
+ private fun doGetReplyTest(subtype: String?) {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, subtype)
+ val queriedName = if (subtype == null) arrayOf("_testservice", "_tcp", "local")
+ else arrayOf(subtype, "_sub", "_testservice", "_tcp", "local")
+
+ val questions = listOf(MdnsPointerRecord(queriedName,
0L /* receiptTimeMillis */,
false /* cacheFlush */,
// TTL and data is empty for a question
@@ -344,7 +434,7 @@
assertEquals(listOf(
MdnsPointerRecord(
- arrayOf("_testservice", "_tcp", "local"),
+ queriedName,
0L /* receiptTimeMillis */,
false /* cacheFlush */,
longTtl,
@@ -405,8 +495,8 @@
@Test
fun testGetConflictingServices() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
val packet = MdnsPacket(
0 /* flags */,
@@ -431,10 +521,38 @@
}
@Test
+ fun testGetConflictingServicesCaseInsensitive() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
+
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MYTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port + 1,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MYOTHERTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ listOf(TextEntry.fromString("somedifferent=entry"))),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(setOf(TEST_SERVICE_ID_1, TEST_SERVICE_ID_2),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
fun testGetConflictingServices_IdenticalService() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
- repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
- repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
val otherTtlMillis = 1234L
val packet = MdnsPacket(
@@ -446,7 +564,7 @@
0L /* receiptTimeMillis */, true /* cacheFlush */,
otherTtlMillis, 0 /* servicePriority */, 0 /* serviceWeight */,
TEST_SERVICE_1.port,
- TEST_HOSTNAME),
+ arrayOf("ANDROID_000102030405060708090A0B0C0D0E0F", "local")),
MdnsTextRecord(
arrayOf("MyOtherTestService", "_testservice", "_tcp", "local"),
0L /* receiptTimeMillis */, true /* cacheFlush */,
@@ -458,12 +576,44 @@
// Above records are identical to the actual registrations: no conflict
assertEquals(emptySet(), repository.getConflictingServices(packet))
}
+
+ @Test
+ fun testGetConflictingServicesCaseInsensitive_IdenticalService() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MYTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MyOtherTestService", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, emptyList()),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ // Above records are identical to the actual registrations: no conflict
+ assertEquals(emptySet(), repository.getConflictingServices(packet))
+ }
}
-private fun MdnsRecordRepository.initWithService(serviceId: Int, serviceInfo: NsdServiceInfo):
- AnnouncementInfo {
+private fun MdnsRecordRepository.initWithService(
+ serviceId: Int,
+ serviceInfo: NsdServiceInfo,
+ subtype: String? = null
+): AnnouncementInfo {
updateAddresses(TEST_ADDRESSES)
- addService(serviceId, serviceInfo)
+ addService(serviceId, serviceInfo, subtype)
val probingInfo = setServiceProbing(serviceId)
assertNotNull(probingInfo)
return onProbingSucceeded(probingInfo)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt
new file mode 100644
index 0000000..8f819b7
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+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.S_V2)
+class MdnsRecordTest {
+
+ @Test
+ fun testPointerRecordHasSubType() {
+ val ptrRecord1 = MdnsPointerRecord(
+ arrayOf("_testtype", "_sub", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ val ptrRecord2 = MdnsPointerRecord(
+ arrayOf("_testtype", "_SUB", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ assertTrue(ptrRecord1.hasSubtype())
+ assertTrue(ptrRecord2.hasSubtype())
+ }
+
+ @Test
+ fun testEqualsCaseInsensitive() {
+ val ptrRecord1 = MdnsPointerRecord(
+ arrayOf("_testtype", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ val ptrRecord2 = MdnsPointerRecord(
+ arrayOf("_testType", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testsErvice", "_testtype", "_Tcp", "local")
+ )
+ assertEquals(ptrRecord1, ptrRecord2)
+ assertEquals(ptrRecord1.hashCode(), ptrRecord2.hashCode())
+
+ val srvRecord1 = MdnsServiceRecord(
+ arrayOf("testservice", "_testtype", "_tcp", "local"),
+ 123 /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 2000 /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ 80 /* port */,
+ arrayOf("hostname")
+ )
+ val srvRecord2 = MdnsServiceRecord(
+ arrayOf("Testservice", "_testtype", "_tcp", "local"),
+ 123 /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 2000 /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ 80 /* port */,
+ arrayOf("Hostname")
+ )
+ assertEquals(srvRecord1, srvRecord2)
+ assertEquals(srvRecord1.hashCode(), srvRecord2.hashCode())
+
+ val nsecRecord1 = MdnsNsecRecord(
+ arrayOf("hostname"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 2000L, /* ttlMillis */
+ arrayOf("hostname"),
+ intArrayOf(1, 2, 3) /* types */
+ )
+ val nsecRecord2 = MdnsNsecRecord(
+ arrayOf("HOSTNAME"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 2000L, /* ttlMillis */
+ arrayOf("HOSTNAME"),
+ intArrayOf(1, 2, 3) /* types */
+ )
+ assertEquals(nsecRecord1, nsecRecord2)
+ assertEquals(nsecRecord1.hashCode(), nsecRecord2.hashCode())
+ }
+
+ @Test
+ fun testLabelsAreSuffix() {
+ val labels1 = arrayOf("a", "b", "c")
+ val labels2 = arrayOf("B", "C")
+ val labels3 = arrayOf("b", "c")
+ val labels4 = arrayOf("b", "d")
+ assertTrue(MdnsRecord.labelsAreSuffix(labels2, labels1))
+ assertTrue(MdnsRecord.labelsAreSuffix(labels3, labels1))
+ assertFalse(MdnsRecord.labelsAreSuffix(labels4, labels1))
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
index b0a1f18..e16c448 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -269,14 +269,9 @@
assertEquals("st=0", textStrings.get(6));
}
- @Test
- public void testDecodeIPv6AnswerPacket() throws IOException {
- MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
- assertNotNull(data6);
-
- responses = decode(decoder, data6);
- assertEquals(1, responses.size());
- MdnsResponse response = responses.valueAt(0);
+ private void verifyResponse(ArraySet<MdnsResponse> responseArraySet) {
+ assertEquals(1, responseArraySet.size());
+ MdnsResponse response = responseArraySet.valueAt(0);
assertTrue(response.isComplete());
MdnsInetAddressRecord inet6AddressRecord = response.getInet6AddressRecord();
@@ -290,6 +285,22 @@
}
@Test
+ public void testDecodeIPv6AnswerPacket() throws IOException {
+ MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
+ assertNotNull(data6);
+ verifyResponse(decode(decoder, data6));
+ }
+
+ @Test
+ public void testDecodeCaseInsensitiveMatch() throws IOException {
+ final String[] castServiceTypeUpperCase =
+ new String[] {"_GOOGLECAST", "_TCP", "LOCAL"};
+ MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, castServiceTypeUpperCase);
+ assertNotNull(data6);
+ verifyResponse(decode(decoder, data6));
+ }
+
+ @Test
public void testIsComplete() {
MdnsResponse response = new MdnsResponse(responses.valueAt(0));
assertTrue(response.isComplete());
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
index 3f5e7a1..dc0e646 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
@@ -228,6 +228,12 @@
final MdnsResponse response = makeCompleteResponse(TEST_TTL_MS);
assertFalse(response.addPointerRecord(response.getPointerRecords().get(0)));
+ final String[] serviceName = new String[] { "MYSERVICE", "_TYPE", "_tcp", "local" };
+ final String[] serviceType = new String[] { "_TYPE", "_tcp", "local" };
+ MdnsPointerRecord pointerRecordCaseInsensitive =
+ new MdnsPointerRecord(serviceType, 0L /* receiptTimeMillis */,
+ false /* cacheFlush */, TEST_TTL_MS, serviceName);
+ assertFalse(response.addPointerRecord(pointerRecordCaseInsensitive));
assertFalse(response.addInet6AddressRecord(response.getInet6AddressRecord()));
assertFalse(response.addInet4AddressRecord(response.getInet4AddressRecord()));
assertFalse(response.setServiceRecord(response.getServiceRecord()));
@@ -270,4 +276,30 @@
// All records were replaced, not added
assertEquals(ttlZeroResponse.getRecords().size(), response.getRecords().size());
}
+
+ @Test
+ public void dropUnmatchedAddressRecords_caseInsensitive() {
+
+ final String[] hostname = new String[] { "MyHostname" };
+ final String[] upperCaseHostName = new String[] { "MYHOSTNAME" };
+ final String[] serviceName = new String[] { "MyService", "_type", "_tcp", "local" };
+ final String[] serviceType = new String[] { "_type", "_tcp", "local" };
+ final MdnsResponse response = new MdnsResponse(/* now= */ 0, serviceName, INTERFACE_INDEX,
+ mNetwork);
+ response.addPointerRecord(new MdnsPointerRecord(serviceType, 0L /* receiptTimeMillis */,
+ false /* cacheFlush */, TEST_TTL_MS, serviceName));
+ response.setServiceRecord(new MdnsServiceRecord(serviceName, 0L /* receiptTimeMillis */,
+ true /* cacheFlush */, TEST_TTL_MS, 0 /* servicePriority */,
+ 0 /* serviceWeight */, 0 /* servicePort */, hostname));
+ response.setTextRecord(new MdnsTextRecord(serviceName, 0L /* receiptTimeMillis */,
+ true /* cacheFlush */, TEST_TTL_MS, emptyList() /* entries */));
+ response.addInet4AddressRecord(new MdnsInetAddressRecord(
+ upperCaseHostName , 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ TEST_TTL_MS, parseNumericAddress("192.0.2.123")));
+ response.addInet6AddressRecord(new MdnsInetAddressRecord(
+ upperCaseHostName, 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ TEST_TTL_MS, parseNumericAddress("2001:db8::123")));
+
+ assertFalse(response.dropUnmatchedAddressRecords());
+ }
}
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceCacheTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceCacheTest.kt
new file mode 100644
index 0000000..f091eea
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceCacheTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.net.Network
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertNotNull
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+private const val SERVICE_NAME_1 = "service-instance-1"
+private const val SERVICE_NAME_2 = "service-instance-2"
+private const val SERVICE_TYPE_1 = "_test1._tcp.local"
+private const val SERVICE_TYPE_2 = "_test2._tcp.local"
+private const val INTERFACE_INDEX = 999
+private const val DEFAULT_TIMEOUT_MS = 2000L
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsServiceCacheTest {
+ private val network = mock(Network::class.java)
+ private val thread = HandlerThread(MdnsServiceCacheTest::class.simpleName)
+ private val handler by lazy {
+ Handler(thread.looper)
+ }
+ private val serviceCache by lazy {
+ MdnsServiceCache(thread.looper)
+ }
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ private fun <T> runningOnHandlerAndReturn(functor: (() -> T)): T {
+ val future = CompletableFuture<T>()
+ handler.post {
+ future.complete(functor())
+ }
+ return future.get(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }
+
+ private fun addOrUpdateService(serviceType: String, network: Network, service: MdnsResponse):
+ Unit = runningOnHandlerAndReturn {
+ serviceCache.addOrUpdateService(serviceType, network, service) }
+
+ private fun removeService(serviceName: String, serviceType: String, network: Network):
+ Unit = runningOnHandlerAndReturn {
+ serviceCache.removeService(serviceName, serviceType, network) }
+
+ private fun getService(serviceName: String, serviceType: String, network: Network):
+ MdnsResponse? = runningOnHandlerAndReturn {
+ serviceCache.getCachedService(serviceName, serviceType, network) }
+
+ private fun getServices(serviceType: String, network: Network): List<MdnsResponse> =
+ runningOnHandlerAndReturn { serviceCache.getCachedServices(serviceType, network) }
+
+ @Test
+ fun testAddAndRemoveService() {
+ addOrUpdateService(SERVICE_TYPE_1, network, createResponse(SERVICE_NAME_1, SERVICE_TYPE_1))
+ var response = getService(SERVICE_NAME_1, SERVICE_TYPE_1, network)
+ assertNotNull(response)
+ assertEquals(SERVICE_NAME_1, response.serviceInstanceName)
+ removeService(SERVICE_NAME_1, SERVICE_TYPE_1, network)
+ response = getService(SERVICE_NAME_1, SERVICE_TYPE_1, network)
+ assertNull(response)
+ }
+
+ @Test
+ fun testGetCachedServices_multipleServiceTypes() {
+ addOrUpdateService(SERVICE_TYPE_1, network, createResponse(SERVICE_NAME_1, SERVICE_TYPE_1))
+ addOrUpdateService(SERVICE_TYPE_1, network, createResponse(SERVICE_NAME_2, SERVICE_TYPE_1))
+ addOrUpdateService(SERVICE_TYPE_2, network, createResponse(SERVICE_NAME_2, SERVICE_TYPE_2))
+
+ val responses1 = getServices(SERVICE_TYPE_1, network)
+ assertEquals(2, responses1.size)
+ assertTrue(responses1.stream().anyMatch { response ->
+ response.serviceInstanceName == SERVICE_NAME_1
+ })
+ assertTrue(responses1.any { response ->
+ response.serviceInstanceName == SERVICE_NAME_2
+ })
+ val responses2 = getServices(SERVICE_TYPE_2, network)
+ assertEquals(1, responses2.size)
+ assertTrue(responses2.any { response ->
+ response.serviceInstanceName == SERVICE_NAME_2
+ })
+
+ removeService(SERVICE_NAME_2, SERVICE_TYPE_1, network)
+ val responses3 = getServices(SERVICE_TYPE_1, network)
+ assertEquals(1, responses3.size)
+ assertTrue(responses3.any { response ->
+ response.serviceInstanceName == SERVICE_NAME_1
+ })
+ val responses4 = getServices(SERVICE_TYPE_2, network)
+ assertEquals(1, responses4.size)
+ assertTrue(responses4.any { response ->
+ response.serviceInstanceName == SERVICE_NAME_2
+ })
+ }
+
+ private fun createResponse(serviceInstanceName: String, serviceType: String) = MdnsResponse(
+ 0 /* now */, "$serviceInstanceName.$serviceType".split(".").toTypedArray(),
+ INTERFACE_INDEX, network)
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 746994f..f15e8ff 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -41,6 +42,8 @@
import android.net.Network;
import android.text.TextUtils;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
import com.android.server.connectivity.mdns.MdnsServiceTypeClient.QueryTaskConfig;
import com.android.testutils.DevSdkIgnoreRule;
@@ -51,6 +54,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
@@ -86,6 +90,7 @@
private static final long TEST_TTL = 120000L;
private static final long TEST_ELAPSED_REALTIME = 123L;
+ private static final long TEST_TIMEOUT_MS = 10_000L;
@Mock
private MdnsServiceBrowserListener mockListenerOne;
@@ -99,6 +104,8 @@
private Network mockNetwork;
@Mock
private MdnsResponseDecoder.Clock mockDecoderClock;
+ @Mock
+ private SharedLog mockSharedLog;
@Captor
private ArgumentCaptor<MdnsServiceInfo> serviceInfoCaptor;
@@ -166,7 +173,7 @@
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, mockNetwork) {
+ mockDecoderClock, mockNetwork, mockSharedLog) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -419,6 +426,34 @@
assertNull(currentThreadExecutor.getAndClearLastScheduledRunnable());
}
+ @Test
+ public void testQueryScheduledWhenAnsweredFromCache() {
+ final MdnsSearchOptions searchOptions = MdnsSearchOptions.getDefaultOptions();
+ client.startSendAndReceive(mockListenerOne, searchOptions);
+ assertNotNull(currentThreadExecutor.getAndClearSubmittedRunnable());
+
+ client.processResponse(createResponse(
+ "service-instance-1", "192.0.2.123", 5353,
+ SERVICE_TYPE_LABELS,
+ Collections.emptyMap(), TEST_TTL), /* interfaceIndex= */ 20, mockNetwork);
+
+ verify(mockListenerOne).onServiceNameDiscovered(any());
+ verify(mockListenerOne).onServiceFound(any());
+
+ // File another identical query
+ client.startSendAndReceive(mockListenerTwo, searchOptions);
+
+ verify(mockListenerTwo).onServiceNameDiscovered(any());
+ verify(mockListenerTwo).onServiceFound(any());
+
+ // This time no query is submitted, only scheduled
+ assertNull(currentThreadExecutor.getAndClearSubmittedRunnable());
+ assertNotNull(currentThreadExecutor.getAndClearLastScheduledRunnable());
+ // This just skips the first query of the first burst
+ assertEquals(MdnsConfigs.timeBetweenQueriesInBurstMs(),
+ currentThreadExecutor.getAndClearLastScheduledDelayInMs());
+ }
+
private static void verifyServiceInfo(MdnsServiceInfo serviceInfo, String serviceName,
String[] serviceType, List<String> ipv4Addresses, List<String> ipv6Addresses, int port,
List<String> subTypes, Map<String, String> attributes, int interfaceIndex,
@@ -701,7 +736,7 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, mockNetwork) {
+ mockDecoderClock, mockNetwork, mockSharedLog) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -740,7 +775,7 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, mockNetwork) {
+ mockDecoderClock, mockNetwork, mockSharedLog) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -773,7 +808,7 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, mockNetwork) {
+ mockDecoderClock, mockNetwork, mockSharedLog) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -898,7 +933,7 @@
@Test
public void testProcessResponse_Resolve() throws Exception {
client = new MdnsServiceTypeClient(
- SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork);
+ SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "};
@@ -917,7 +952,7 @@
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
// Send twice for IPv4 and IPv6
inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(),
- eq(null) /* network */);
+ eq(mockNetwork));
final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
@@ -952,7 +987,7 @@
ArgumentCaptor.forClass(DatagramPacket.class);
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(addressQueryCaptor.capture(),
- eq(null) /* network */);
+ eq(mockNetwork));
final MdnsPacket addressQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(addressQueryCaptor.getValue()));
@@ -995,16 +1030,17 @@
@Test
public void testProcessResponse_ResolveExcludesOtherServices() {
client = new MdnsServiceTypeClient(
- SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork);
+ SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
final String requestedInstance = "instance1";
final String otherInstance = "instance2";
final String ipV4Address = "192.0.2.0";
final String ipV6Address = "2001:db8::";
+ final String capitalizedRequestInstance = "Instance1";
final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
// Use different case in the options
- .setResolveInstanceName("Instance1").build();
+ .setResolveInstanceName(capitalizedRequestInstance).build();
client.startSendAndReceive(mockListenerOne, resolveOptions);
client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
@@ -1032,8 +1068,9 @@
Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
// mockListenerOne gets notified for the requested instance
- verify(mockListenerOne).onServiceNameDiscovered(matchServiceName(requestedInstance));
- verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+ verify(mockListenerOne).onServiceNameDiscovered(
+ matchServiceName(capitalizedRequestInstance));
+ verify(mockListenerOne).onServiceFound(matchServiceName(capitalizedRequestInstance));
// ...but does not get any callback for the other instance
verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
@@ -1044,8 +1081,9 @@
// mockListenerTwo gets notified for both though
final InOrder inOrder = inOrder(mockListenerTwo);
inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(capitalizedRequestInstance));
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(capitalizedRequestInstance));
inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
@@ -1053,6 +1091,153 @@
inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
}
+ @Test
+ public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() {
+ client = new MdnsServiceTypeClient(
+ SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
+
+ final String matchingInstance = "instance1";
+ final String subtype = "_subtype";
+ final String otherInstance = "instance2";
+ final String ipV4Address = "192.0.2.0";
+ final String ipV6Address = "2001:db8::";
+
+ final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
+ // Search with different case. Note MdnsSearchOptions subtype doesn't start with "_"
+ .addSubtype("Subtype").build();
+
+ client.startSendAndReceive(mockListenerOne, options);
+ client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+
+ // Complete response from instanceName
+ final MdnsPacket packetWithoutSubtype = createResponse(
+ matchingInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL);
+ final MdnsPointerRecord originalPtr = (MdnsPointerRecord) CollectionUtils.findFirst(
+ packetWithoutSubtype.answers, r -> r instanceof MdnsPointerRecord);
+
+ // Add a subtype PTR record
+ final ArrayList<MdnsRecord> newAnswers = new ArrayList<>(packetWithoutSubtype.answers);
+ newAnswers.add(new MdnsPointerRecord(
+ // PTR should be _subtype._sub._type._tcp.local -> instance1._type._tcp.local
+ Stream.concat(Stream.of(subtype, "_sub"), Arrays.stream(SERVICE_TYPE_LABELS))
+ .toArray(String[]::new),
+ originalPtr.getReceiptTime(), originalPtr.getCacheFlush(), originalPtr.getTtl(),
+ originalPtr.getPointer()));
+ final MdnsPacket packetWithSubtype = new MdnsPacket(
+ packetWithoutSubtype.flags,
+ packetWithoutSubtype.questions,
+ newAnswers,
+ packetWithoutSubtype.authorityRecords,
+ packetWithoutSubtype.additionalRecords);
+ client.processResponse(packetWithSubtype, INTERFACE_INDEX, mockNetwork);
+
+ // Complete response from otherInstanceName, without subtype
+ client.processResponse(createResponse(
+ otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL),
+ INTERFACE_INDEX, mockNetwork);
+
+ // Address update from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+
+ // Goodbye from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
+
+ // mockListenerOne gets notified for the requested instance
+ final ArgumentMatcher<MdnsServiceInfo> subtypeInstanceMatcher = info ->
+ info.getServiceInstanceName().equals(matchingInstance)
+ && info.getSubtypes().equals(Collections.singletonList(subtype));
+ verify(mockListenerOne).onServiceNameDiscovered(argThat(subtypeInstanceMatcher));
+ verify(mockListenerOne).onServiceFound(argThat(subtypeInstanceMatcher));
+
+ // ...but does not get any callback for the other instance
+ verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceUpdated(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
+
+ // mockListenerTwo gets notified for both though
+ final InOrder inOrder = inOrder(mockListenerTwo);
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(argThat(subtypeInstanceMatcher));
+ inOrder.verify(mockListenerTwo).onServiceFound(argThat(subtypeInstanceMatcher));
+
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
+ }
+
+ @Test
+ public void testNotifySocketDestroyed() throws Exception {
+ client = new MdnsServiceTypeClient(
+ SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
+
+ final String requestedInstance = "instance1";
+ final String otherInstance = "instance2";
+ final String ipV4Address = "192.0.2.0";
+
+ final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
+ .setResolveInstanceName("instance1").build();
+
+ client.startSendAndReceive(mockListenerOne, resolveOptions);
+ // Ensure the first task is executed so it schedules a future task
+ currentThreadExecutor.getAndClearSubmittedFuture().get(
+ TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+
+ // Filing the second request cancels the first future
+ verify(expectedSendFutures[0]).cancel(true);
+
+ // Ensure it gets executed too
+ currentThreadExecutor.getAndClearSubmittedFuture().get(
+ TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+ // Complete response from instanceName
+ client.processResponse(createResponse(
+ requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL),
+ INTERFACE_INDEX, mockNetwork);
+
+ // Complete response from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL),
+ INTERFACE_INDEX, mockNetwork);
+
+ verify(expectedSendFutures[1], never()).cancel(true);
+ client.notifySocketDestroyed();
+ verify(expectedSendFutures[1]).cancel(true);
+
+ // mockListenerOne gets notified for the requested instance
+ final InOrder inOrder1 = inOrder(mockListenerOne);
+ inOrder1.verify(mockListenerOne).onServiceNameDiscovered(
+ matchServiceName(requestedInstance));
+ inOrder1.verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+ inOrder1.verify(mockListenerOne).onServiceRemoved(matchServiceName(requestedInstance));
+ inOrder1.verify(mockListenerOne).onServiceNameRemoved(matchServiceName(requestedInstance));
+ verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceNameRemoved(matchServiceName(otherInstance));
+
+ // mockListenerTwo gets notified for both though
+ final InOrder inOrder2 = inOrder(mockListenerTwo);
+ inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(requestedInstance));
+ inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+ inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
+ inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
+ inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
+ inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance));
+ inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance));
+ }
+
private static MdnsServiceInfo matchServiceName(String name) {
return argThat(info -> info.getServiceInstanceName().equals(name));
}
@@ -1070,17 +1255,17 @@
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
if (expectsUnicastResponse) {
verify(mockSocketClient).sendUnicastPacket(
- expectedIPv4Packets[index], null /* network */);
+ expectedIPv4Packets[index], mockNetwork);
if (multipleSocketDiscovery) {
verify(mockSocketClient).sendUnicastPacket(
- expectedIPv6Packets[index], null /* network */);
+ expectedIPv6Packets[index], mockNetwork);
}
} else {
verify(mockSocketClient).sendMulticastPacket(
- expectedIPv4Packets[index], null /* network */);
+ expectedIPv4Packets[index], mockNetwork);
if (multipleSocketDiscovery) {
verify(mockSocketClient).sendMulticastPacket(
- expectedIPv6Packets[index], null /* network */);
+ expectedIPv6Packets[index], mockNetwork);
}
}
}
@@ -1091,6 +1276,7 @@
private long lastScheduledDelayInMs;
private Runnable lastScheduledRunnable;
private Runnable lastSubmittedRunnable;
+ private Future<?> lastSubmittedFuture;
private int futureIndex;
FakeExecutor() {
@@ -1102,6 +1288,7 @@
public Future<?> submit(Runnable command) {
Future<?> future = super.submit(command);
lastSubmittedRunnable = command;
+ lastSubmittedFuture = future;
return future;
}
@@ -1133,6 +1320,12 @@
lastSubmittedRunnable = null;
return val;
}
+
+ Future<?> getAndClearSubmittedFuture() {
+ Future<?> val = lastSubmittedFuture;
+ lastSubmittedFuture = null;
+ return val;
+ }
}
private MdnsPacket createResponse(
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index 2d73c98..4b87556 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -111,8 +111,9 @@
private MdnsSocketProvider mSocketProvider;
private NetworkCallback mNetworkCallback;
private TetheringEventCallback mTetheringEventCallback;
+ private SharedLog mLog = new SharedLog("MdnsSocketProviderTest");
- private TestNetLinkMonitor mTestSocketNetLinkMonitor;
+ private TestNetlinkMonitor mTestSocketNetLinkMonitor;
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
@@ -147,13 +148,13 @@
doReturn(mTestSocketNetLinkMonitor).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
doAnswer(inv -> {
- mTestSocketNetLinkMonitor = new TestNetLinkMonitor(inv.getArgument(0),
+ mTestSocketNetLinkMonitor = new TestNetlinkMonitor(inv.getArgument(0),
inv.getArgument(1),
inv.getArgument(2));
return mTestSocketNetLinkMonitor;
}).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
- mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps);
+ mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog);
}
private void startMonitoringSockets() {
@@ -174,8 +175,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
}
- private static class TestNetLinkMonitor extends SocketNetlinkMonitor {
- TestNetLinkMonitor(@NonNull Handler handler,
+ private static class TestNetlinkMonitor extends SocketNetlinkMonitor {
+ TestNetlinkMonitor(@NonNull Handler handler,
@NonNull SharedLog log,
@Nullable MdnsSocketProvider.NetLinkMonitorCallBack cb) {
super(handler, log, cb);
@@ -348,8 +349,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- // Expect the socket destroy for tethered interface.
- testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
+ // There was still a tethered interface, but no callback should be sent once unregistered
+ testCallback3.expectedNoCallback();
}
private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(
@@ -527,7 +528,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- testCallback.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
+ // No callback sent when unregistered
+ testCallback.expectedNoCallback();
verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, times(1)).unregisterTetheringEventCallback(any());
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
new file mode 100644
index 0000000..c62a081
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitorTest.kt
@@ -0,0 +1,90 @@
+package com.android.server.connectivity.mdns.internal
+
+import android.net.LinkAddress
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import android.system.OsConstants
+import com.android.net.module.util.SharedLog
+import com.android.net.module.util.netlink.NetlinkConstants
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage
+import com.android.net.module.util.netlink.StructIfaddrMsg
+import com.android.net.module.util.netlink.StructNlMsgHdr
+import com.android.server.connectivity.mdns.MdnsAdvertiserTest
+import com.android.server.connectivity.mdns.MdnsSocketProvider
+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.ArgumentMatchers.eq
+import org.mockito.Mockito
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+private val LINKADDRV4 = LinkAddress("192.0.2.0/24")
+private val IFACE_IDX = 32
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+internal class SocketNetlinkMonitorTest {
+ private val thread = HandlerThread(MdnsAdvertiserTest::class.simpleName)
+ private val sharedlog = Mockito.mock(SharedLog::class.java)
+ private val netlinkMonitorCallBack =
+ Mockito.mock(MdnsSocketProvider.NetLinkMonitorCallBack::class.java)
+
+ @Before
+ fun setUp() {
+ thread.start()
+ }
+
+ @After
+ fun tearDown() {
+ thread.quitSafely()
+ }
+
+ @Test
+ fun testHandleDeprecatedNetlinkMessage() {
+ val socketNetlinkMonitor = SocketNetlinkMonitor(Handler(thread.looper), sharedlog,
+ netlinkMonitorCallBack)
+ val nlmsghdr = StructNlMsgHdr().apply {
+ nlmsg_type = NetlinkConstants.RTM_NEWADDR
+ nlmsg_flags = (StructNlMsgHdr.NLM_F_REQUEST.toInt()
+ or StructNlMsgHdr.NLM_F_ACK.toInt()).toShort()
+ nlmsg_seq = 1
+ }
+ val structIfaddrMsg = StructIfaddrMsg(OsConstants.AF_INET.toShort(),
+ LINKADDRV4.prefixLength.toShort(),
+ LINKADDRV4.flags.toShort(),
+ LINKADDRV4.scope.toShort(), IFACE_IDX)
+ // If the LinkAddress is not preferred, RTM_NEWADDR will not trigger
+ // addOrUpdateInterfaceAddress() callback.
+ val deprecatedAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+ socketNetlinkMonitor.processNetlinkMessage(deprecatedAddNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack, never()).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+
+ // If the LinkAddress is preferred, RTM_NEWADDR will trigger addOrUpdateInterfaceAddress()
+ // callback.
+ val preferredAddNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_OPTIMISTIC)
+ socketNetlinkMonitor.processNetlinkMessage(preferredAddNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack).addOrUpdateInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+
+ // Even if the LinkAddress is not preferred, RTM_DELADDR will trigger
+ // deleteInterfaceAddress() callback.
+ nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR
+ val deprecatedDelNetLinkMessage = RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg,
+ LINKADDRV4.address, null /* structIfacacheInfo */,
+ LINKADDRV4.flags or OsConstants.IFA_F_DEPRECATED)
+ socketNetlinkMonitor.processNetlinkMessage(deprecatedDelNetLinkMessage, 0L /* whenMs */)
+ verify(netlinkMonitorCallBack).deleteInterfaceAddress(eq(IFACE_IDX),
+ argThat { it.address == LINKADDRV4.address })
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
new file mode 100644
index 0000000..f705bcb
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns.util
+
+import android.os.Build
+import com.android.server.connectivity.mdns.util.MdnsUtils.equalsDnsLabelIgnoreDnsCase
+import com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase
+import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLabelsLowerCase
+import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLowerCase
+import com.android.server.connectivity.mdns.util.MdnsUtils.truncateServiceName
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsUtilsTest {
+ @Test
+ fun testToDnsLowerCase() {
+ assertEquals("test", toDnsLowerCase("TEST"))
+ assertEquals("test", toDnsLowerCase("TeSt"))
+ assertEquals("test", toDnsLowerCase("test"))
+ assertEquals("tÉst", toDnsLowerCase("TÉST"))
+ assertEquals("ţést", toDnsLowerCase("ţést"))
+ // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
+ // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
+ assertEquals("test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
+ toDnsLowerCase("Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- "))
+ // Also test some characters where the first surrogate is not \ud800
+ assertEquals("test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
+ toDnsLowerCase("Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"))
+ }
+
+ @Test
+ fun testToDnsLabelsLowerCase() {
+ assertArrayEquals(arrayOf("test", "tÉst", "ţést"),
+ toDnsLabelsLowerCase(arrayOf("TeSt", "TÉST", "ţést")))
+ }
+
+ @Test
+ fun testEqualsIgnoreDnsCase() {
+ assertTrue(equalsIgnoreDnsCase("TEST", "Test"))
+ assertTrue(equalsIgnoreDnsCase("TEST", "test"))
+ assertTrue(equalsIgnoreDnsCase("test", "TeSt"))
+ assertTrue(equalsIgnoreDnsCase("Tést", "tést"))
+ assertFalse(equalsIgnoreDnsCase("ŢÉST", "ţést"))
+ // Unicode characters 0x10000 (𐀀), 0x10001 (𐀁), 0x10041 (𐁁)
+ // Note the last 2 bytes of 0x10041 are identical to 'A', but it should remain unchanged.
+ assertTrue(equalsIgnoreDnsCase("test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- ",
+ "Test: -->\ud800\udc00 \ud800\udc01 \ud800\udc41<-- "))
+ // Also test some characters where the first surrogate is not \ud800
+ assertTrue(equalsIgnoreDnsCase("test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<",
+ "Test: >\ud83c\udff4\udb40\udc67\udb40\udc62\udb40" +
+ "\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f<"))
+ }
+
+ @Test
+ fun testTruncateServiceName() {
+ assertEquals(truncateServiceName("测试abcde", 7), "测试a")
+ assertEquals(truncateServiceName("测试abcde", 100), "测试abcde")
+ }
+
+ @Test
+ fun testEqualsLabelIgnoreDnsCase() {
+ assertTrue(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "tést")))
+ }
+
+ @Test
+ fun testTypeEqualsOrIsSubtype() {
+ assertTrue(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_type", "_tcp", "local"),
+ arrayOf("_type", "_TCP", "local")))
+ assertTrue(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_type", "_tcp", "local"),
+ arrayOf("a", "_SUB", "_type", "_TCP", "local")))
+ assertFalse(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_sub", "_type", "_tcp", "local"),
+ arrayOf("_type", "_TCP", "local")))
+ assertFalse(MdnsUtils.typeEqualsOrIsSubtype(
+ arrayOf("a", "_other", "_type", "_tcp", "local"),
+ arrayOf("a", "_SUB", "_type", "_TCP", "local")))
+ }
+}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 04163fd..b8b0289 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -96,7 +96,6 @@
import android.content.Intent;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.net.ConnectivityResources;
import android.net.DataUsageRequest;
import android.net.INetd;
import android.net.INetworkStatsSession;
@@ -145,6 +144,7 @@
import com.android.net.module.util.bpf.CookieTagMapKey;
import com.android.net.module.util.bpf.CookieTagMapValue;
import com.android.server.BpfNetMaps;
+import com.android.server.connectivity.ConnectivityResources;
import com.android.server.net.NetworkStatsService.AlertObserver;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -1926,12 +1926,17 @@
// Templates w/o wifi network keys can query stats as usual.
assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+ // Templates for test network does not need to enforce location permission.
+ final NetworkTemplate templateTestIface1 = new NetworkTemplate.Builder(MATCH_TEST)
+ .setWifiNetworkKeys(Set.of(TEST_IFACE)).build();
+ assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
doReturn(true).when(mLocationPermissionChecker)
.checkCallersLocationPermission(any(), any(), anyInt(), anyBoolean(), any());
assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+ assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
}
/**
diff --git a/tests/unit/vpn-jarjar-rules.txt b/tests/unit/vpn-jarjar-rules.txt
index 16661b9..1a6bddc 100644
--- a/tests/unit/vpn-jarjar-rules.txt
+++ b/tests/unit/vpn-jarjar-rules.txt
@@ -1,4 +1,4 @@
# Only keep classes imported by ConnectivityServiceTest
-keep com.android.server.VpnManagerService
keep com.android.server.connectivity.Vpn
-keep com.android.server.connectivity.VpnProfileStore
\ No newline at end of file
+keep com.android.server.connectivity.VpnProfileStore
+keep com.android.server.net.LockdownVpnTracker