Merge "Reland "Bundle Cronet's boringssl""
diff --git a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
index 77cb30e..219db61 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
@@ -32,37 +32,37 @@
val options =
ConnectionMigrationOptions.Builder().build()
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.allowNonDefaultNetworkUsageEnabled)
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.defaultNetworkMigrationEnabled)
- assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.pathDegradationMigrationEnabled)
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.allowNonDefaultNetworkUsage)
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.defaultNetworkMigration)
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.pathDegradationMigration)
}
@Test
fun testConnectionMigrationOptions_enableDefaultNetworkMigration_returnSetValue() {
val options =
ConnectionMigrationOptions.Builder()
- .setDefaultNetworkMigrationEnabled(MIGRATION_OPTION_ENABLED)
+ .setDefaultNetworkMigration(MIGRATION_OPTION_ENABLED)
.build()
- assertEquals(MIGRATION_OPTION_ENABLED, options.defaultNetworkMigrationEnabled)
+ assertEquals(MIGRATION_OPTION_ENABLED, options.defaultNetworkMigration)
}
@Test
fun testConnectionMigrationOptions_enablePathDegradationMigration_returnSetValue() {
val options =
ConnectionMigrationOptions.Builder()
- .setPathDegradationMigrationEnabled(MIGRATION_OPTION_ENABLED)
+ .setPathDegradationMigration(MIGRATION_OPTION_ENABLED)
.build()
- assertEquals(MIGRATION_OPTION_ENABLED, options.pathDegradationMigrationEnabled)
+ assertEquals(MIGRATION_OPTION_ENABLED, options.pathDegradationMigration)
}
@Test
fun testConnectionMigrationOptions_allowNonDefaultNetworkUsage_returnSetValue() {
val options =
ConnectionMigrationOptions.Builder()
- .setAllowNonDefaultNetworkUsageEnabled(MIGRATION_OPTION_ENABLED).build()
+ .setAllowNonDefaultNetworkUsage(MIGRATION_OPTION_ENABLED).build()
- assertEquals(MIGRATION_OPTION_ENABLED, options.allowNonDefaultNetworkUsageEnabled)
+ assertEquals(MIGRATION_OPTION_ENABLED, options.allowNonDefaultNetworkUsage)
}
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt
index 8af544e..6f4a979 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/DnsOptionsTest.kt
@@ -34,22 +34,22 @@
fun testDnsOptions_defaultValues() {
val options = DnsOptions.Builder().build()
- assertEquals(DNS_OPTION_UNSPECIFIED, options.persistHostCacheEnabled)
+ assertEquals(DNS_OPTION_UNSPECIFIED, options.persistHostCache)
assertNull(options.persistHostCachePeriod)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.staleDnsEnabled)
+ assertEquals(DNS_OPTION_UNSPECIFIED, options.staleDns)
assertNull(options.staleDnsOptions)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.useHttpStackDnsResolverEnabled)
+ assertEquals(DNS_OPTION_UNSPECIFIED, options.useHttpStackDnsResolver)
assertEquals(DNS_OPTION_UNSPECIFIED,
- options.preestablishConnectionsToStaleDnsResultsEnabled)
+ options.preestablishConnectionsToStaleDnsResults)
}
@Test
fun testDnsOptions_persistHostCache_returnSetValue() {
val options = DnsOptions.Builder()
- .setPersistHostCacheEnabled(DNS_OPTION_ENABLED)
+ .setPersistHostCache(DNS_OPTION_ENABLED)
.build()
- assertEquals(DNS_OPTION_ENABLED, options.persistHostCacheEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.persistHostCache)
}
@Test
@@ -63,43 +63,43 @@
@Test
fun testDnsOptions_enableStaleDns_returnSetValue() {
val options = DnsOptions.Builder()
- .setStaleDnsEnabled(DNS_OPTION_ENABLED)
+ .setStaleDns(DNS_OPTION_ENABLED)
.build()
- assertEquals(DNS_OPTION_ENABLED, options.staleDnsEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.staleDns)
}
@Test
fun testDnsOptions_useHttpStackDnsResolver_returnsSetValue() {
val options = DnsOptions.Builder()
- .setUseHttpStackDnsResolverEnabled(DNS_OPTION_ENABLED)
+ .setUseHttpStackDnsResolver(DNS_OPTION_ENABLED)
.build()
- assertEquals(DNS_OPTION_ENABLED, options.useHttpStackDnsResolverEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.useHttpStackDnsResolver)
}
@Test
fun testDnsOptions_preestablishConnectionsToStaleDnsResults_returnsSetValue() {
val options = DnsOptions.Builder()
- .setPreestablishConnectionsToStaleDnsResultsEnabled(DNS_OPTION_ENABLED)
+ .setPreestablishConnectionsToStaleDnsResults(DNS_OPTION_ENABLED)
.build()
assertEquals(DNS_OPTION_ENABLED,
- options.preestablishConnectionsToStaleDnsResultsEnabled)
+ options.preestablishConnectionsToStaleDnsResults)
}
@Test
fun testDnsOptions_setStaleDnsOptions_returnsSetValues() {
val staleOptions = DnsOptions.StaleDnsOptions.Builder()
- .setAllowCrossNetworkUsageEnabled(DNS_OPTION_ENABLED)
+ .setAllowCrossNetworkUsage(DNS_OPTION_ENABLED)
.setFreshLookupTimeout(Duration.ofMillis(1234))
.build()
val options = DnsOptions.Builder()
- .setStaleDnsEnabled(DNS_OPTION_ENABLED)
+ .setStaleDns(DNS_OPTION_ENABLED)
.setStaleDnsOptions(staleOptions)
.build()
- assertEquals(DNS_OPTION_ENABLED, options.staleDnsEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.staleDns)
assertEquals(staleOptions, options.staleDnsOptions)
}
@@ -107,18 +107,18 @@
fun testStaleDnsOptions_defaultValues() {
val options = DnsOptions.StaleDnsOptions.Builder().build()
- assertEquals(DNS_OPTION_UNSPECIFIED, options.allowCrossNetworkUsageEnabled)
+ assertEquals(DNS_OPTION_UNSPECIFIED, options.allowCrossNetworkUsage)
assertNull(options.freshLookupTimeout)
assertNull(options.maxExpiredDelay)
- assertEquals(DNS_OPTION_UNSPECIFIED, options.useStaleOnNameNotResolvedEnabled)
+ assertEquals(DNS_OPTION_UNSPECIFIED, options.useStaleOnNameNotResolved)
}
@Test
fun testStaleDnsOptions_allowCrossNetworkUsage_returnsSetValue() {
val options = DnsOptions.StaleDnsOptions.Builder()
- .setAllowCrossNetworkUsageEnabled(DNS_OPTION_ENABLED).build()
+ .setAllowCrossNetworkUsage(DNS_OPTION_ENABLED).build()
- assertEquals(DNS_OPTION_ENABLED, options.allowCrossNetworkUsageEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.allowCrossNetworkUsage)
}
@Test
@@ -133,10 +133,10 @@
@Test
fun testStaleDnsOptions_useStaleOnNameNotResolved_returnsSetValue() {
val options = DnsOptions.StaleDnsOptions.Builder()
- .setUseStaleOnNameNotResolvedEnabled(DNS_OPTION_ENABLED)
+ .setUseStaleOnNameNotResolved(DNS_OPTION_ENABLED)
.build()
- assertEquals(DNS_OPTION_ENABLED, options.useStaleOnNameNotResolvedEnabled)
+ assertEquals(DNS_OPTION_ENABLED, options.useStaleOnNameNotResolved)
}
@Test
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 0be3ea1..c561ee7 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/HttpEngineTest.java
@@ -105,8 +105,9 @@
@Test
public void testHttpEngine_EnableHttpCache() {
- // We need a server which sets cache-control != no-cache.
- String url = "https://www.example.com";
+ String url = mTestServer.getCacheableTestDownloadUrl(
+ /* downloadId */ "cacheable-download",
+ /* numBytes */ 10);
mEngine =
mEngineBuilder
.setStoragePath(mContext.getApplicationInfo().dataDir)
@@ -118,9 +119,7 @@
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);
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
UrlResponseInfo info = mCallback.mResponseInfo;
assumeOKStatusCode(info);
assertFalse(info.wasCached());
@@ -129,7 +128,7 @@
builder = mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
mRequest = builder.build();
mRequest.start();
- mCallback.assumeCallback(ResponseStep.ON_SUCCEEDED);
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
info = mCallback.mResponseInfo;
assertOKStatusCode(info);
assertTrue(info.wasCached());
@@ -153,11 +152,12 @@
@Test
public void testHttpEngine_EnablePublicKeyPinningBypassForLocalTrustAnchors() {
+ String url = mTestServer.getSuccessUrl();
// For known hosts, requests should succeed whether we're bypassing the local trust anchor
// or not.
mEngine = mEngineBuilder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(false).build();
UrlRequest.Builder builder =
- mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
+ mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
mRequest = builder.build();
mRequest.start();
mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
@@ -165,7 +165,7 @@
mEngine.shutdown();
mEngine = mEngineBuilder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(true).build();
mCallback = new TestUrlRequestCallback();
- builder = mEngine.newUrlRequestBuilder(URL, mCallback.getExecutor(), mCallback);
+ builder = mEngine.newUrlRequestBuilder(url, mCallback.getExecutor(), mCallback);
mRequest = builder.build();
mRequest.start();
mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
@@ -211,6 +211,7 @@
@Test
public void testHttpEngine_GetDefaultUserAgent() throws Exception {
assertThat(mEngineBuilder.getDefaultUserAgent(), containsString("AndroidHttpClient"));
+ assertThat(mEngineBuilder.getDefaultUserAgent()).contains(HttpEngine.getVersionString());
}
@Test
@@ -333,4 +334,9 @@
UrlResponseInfo info = mCallback.mResponseInfo;
assertOKStatusCode(info);
}
-}
\ No newline at end of file
+
+ @Test
+ public void getVersionString_notEmpty() {
+ assertThat(HttpEngine.getVersionString()).isNotEmpty();
+ }
+}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt
new file mode 100644
index 0000000..4b7aa14
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/QuicExceptionTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http.cts
+
+import android.net.http.QuicException
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class QuicExceptionTest {
+
+ @Test
+ fun testQuicException_returnsInputParameters() {
+ val message = "failed"
+ val cause = Throwable("thrown")
+ val quicException =
+ object : QuicException(message, cause) {
+ override fun getErrorCode() = 0
+ override fun isImmediatelyRetryable() = false
+ }
+
+ assertEquals(message, quicException.message)
+ assertEquals(cause, quicException.cause)
+ }
+
+ // TODO: add test for QuicException triggered from HttpEngine
+}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
index a05aecd..5f1979f 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
@@ -19,6 +19,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import java.time.Duration
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,7 +33,10 @@
assertThat(quicOptions.allowedQuicHosts).isEmpty()
assertThat(quicOptions.handshakeUserAgent).isNull()
assertThat(quicOptions.idleConnectionTimeout).isNull()
- assertThat(quicOptions.inMemoryServerConfigsCacheSize).isNull()
+ assertFalse(quicOptions.hasInMemoryServerConfigsCacheSize())
+ assertFailsWith(IllegalStateException::class) {
+ quicOptions.inMemoryServerConfigsCacheSize
+ }
}
@Test
@@ -61,6 +67,7 @@
val quicOptions = QuicOptions.Builder()
.setInMemoryServerConfigsCacheSize(42)
.build()
+ assertTrue(quicOptions.hasInMemoryServerConfigsCacheSize())
assertThat(quicOptions.inMemoryServerConfigsCacheSize)
.isEqualTo(42)
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
index 2ec035b..e4949d3 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.net.http.HeaderBlock;
import android.net.http.HttpEngine;
import android.net.http.HttpException;
import android.net.http.InlineExecutionProhibitedException;
@@ -43,6 +44,7 @@
import android.net.http.cts.util.TestUrlRequestCallback;
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
import android.net.http.cts.util.UploadDataProviders;
+import android.webkit.cts.CtsTestServer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -59,11 +61,16 @@
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
@RunWith(AndroidJUnit4.class)
public class UrlRequestTest {
@@ -288,6 +295,24 @@
}
@Test
+ public void testUrlRequest_redirects() throws Exception {
+ int expectedNumRedirects = 5;
+ String url =
+ mTestServer.getRedirectingAssetUrl("html/hello_world.html", expectedNumRedirects);
+
+ UrlRequest request = createUrlRequestBuilder(url).build();
+ request.start();
+
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+ UrlResponseInfo info = mCallback.mResponseInfo;
+ assertOKStatusCode(info);
+ assertThat(mCallback.mResponseAsString).contains("hello world");
+ assertThat(info.getUrlChain()).hasSize(expectedNumRedirects + 1);
+ assertThat(info.getUrlChain().get(0)).isEqualTo(url);
+ assertThat(info.getUrlChain().get(expectedNumRedirects)).isEqualTo(info.getUrl());
+ }
+
+ @Test
public void testUrlRequestPost_withRedirect() throws Exception {
String body = Strings.repeat(
"Hello, this is a really interesting body, so write this 100 times.", 100);
@@ -312,6 +337,50 @@
assertThat(mCallback.mResponseAsString).isEqualTo(body);
}
+ @Test
+ public void testUrlRequest_customHeaders() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoHeadersUrl());
+
+ List<Map.Entry<String, String>> expectedHeaders = Arrays.asList(
+ Map.entry("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
+ Map.entry("Max-Forwards", "10"),
+ Map.entry("X-Client-Data", "random custom header content"));
+
+ for (Map.Entry<String, String> header : expectedHeaders) {
+ builder.addHeader(header.getKey(), header.getValue());
+ }
+
+ builder.build().start();
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+
+ assertOKStatusCode(mCallback.mResponseInfo);
+
+ List<Map.Entry<String, String>> echoedHeaders =
+ extractEchoedHeaders(mCallback.mResponseInfo.getHeaders());
+
+ // The implementation might decide to add more headers like accepted encodings it handles
+ // internally so the server is likely to see more headers than explicitly set
+ // by the developer.
+ assertThat(echoedHeaders)
+ .containsAtLeastElementsIn(expectedHeaders);
+ }
+
+ private static List<Map.Entry<String, String>> extractEchoedHeaders(HeaderBlock headers) {
+ return headers.getAsList()
+ .stream()
+ .flatMap(input -> {
+ if (input.getKey().startsWith(CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX)) {
+ String strippedKey =
+ input.getKey().substring(
+ CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX.length());
+ return Stream.of(Map.entry(strippedKey, input.getValue()));
+ } else {
+ return Stream.empty();
+ }
+ })
+ .collect(Collectors.toList());
+ }
+
private static class StubUrlRequestCallback implements UrlRequest.Callback {
@Override
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index d2f6d15..8810a8c 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -61,9 +61,13 @@
"modules-utils-build",
"modules-utils-statemachine",
"networkstack-client",
+ // AIDL tetheroffload implementation
+ "android.hardware.tetheroffload-V1-java",
+ // HIDL tetheroffload implementation
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.tetheroffload.control-V1.1-java",
+ "android.hidl.manager-V1.2-java",
"net-utils-framework-common",
"net-utils-device-common",
"net-utils-device-common-bpf",
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index b99c9e4..a3756e0 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -127,7 +127,7 @@
out: ["framework_tethering_jarjar_rules.txt"],
cmd: "$(location jarjar-rules-generator) " +
"$(location :framework-tethering-pre-jarjar{.jar}) " +
- "--apistubs $(location :framework-tethering.stubs.module_lib{.jar}) " +
+ "--apistubs $(location :framework-tethering.stubs.module_lib{.jar}) " +
"--prefix android.net.http.internal " +
"--excludes $(location jarjar-excludes.txt) " +
"--output $(out)",
@@ -151,6 +151,7 @@
filegroup {
name: "framework-tethering-srcs",
+ defaults: ["framework-sources-module-defaults"],
srcs: [
"src/**/*.aidl",
"src/**/*.java",
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt b/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
index 66a0295..333ea1c 100644
--- a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
+++ b/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
@@ -51,9 +51,9 @@
}
public class ConnectionMigrationOptions {
- method public int getAllowNonDefaultNetworkUsageEnabled();
- method public int getDefaultNetworkMigrationEnabled();
- method public int getPathDegradationMigrationEnabled();
+ 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
@@ -62,18 +62,18 @@
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 setAllowNonDefaultNetworkUsageEnabled(int);
- method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setDefaultNetworkMigrationEnabled(int);
- method @NonNull public android.net.http.ConnectionMigrationOptions.Builder setPathDegradationMigrationEnabled(int);
+ 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 getPersistHostCacheEnabled();
+ method public int getPersistHostCache();
method @Nullable public java.time.Duration getPersistHostCachePeriod();
- method public int getPreestablishConnectionsToStaleDnsResultsEnabled();
- method public int getStaleDnsEnabled();
+ method public int getPreestablishConnectionsToStaleDnsResults();
+ method public int getStaleDns();
method @Nullable public android.net.http.DnsOptions.StaleDnsOptions getStaleDnsOptions();
- method public int getUseHttpStackDnsResolverEnabled();
+ 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
@@ -82,28 +82,28 @@
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 setPersistHostCacheEnabled(int);
+ 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 setPreestablishConnectionsToStaleDnsResultsEnabled(int);
- method @NonNull public android.net.http.DnsOptions.Builder setStaleDnsEnabled(int);
+ 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 setUseHttpStackDnsResolverEnabled(int);
+ method @NonNull public android.net.http.DnsOptions.Builder setUseHttpStackDnsResolver(int);
}
public static class DnsOptions.StaleDnsOptions {
- method public int getAllowCrossNetworkUsageEnabled();
+ method public int getAllowCrossNetworkUsage();
method @Nullable public java.time.Duration getFreshLookupTimeout();
method @Nullable public java.time.Duration getMaxExpiredDelay();
- method public int getUseStaleOnNameNotResolvedEnabled();
+ 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 setAllowCrossNetworkUsageEnabled(int);
+ 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 setUseStaleOnNameNotResolvedEnabled(int);
+ method @NonNull public android.net.http.DnsOptions.StaleDnsOptions.Builder setUseStaleOnNameNotResolved(int);
}
public abstract class HeaderBlock {
@@ -177,7 +177,8 @@
method @NonNull public java.util.Set<java.lang.String> getAllowedQuicHosts();
method @Nullable public String getHandshakeUserAgent();
method @Nullable public java.time.Duration getIdleConnectionTimeout();
- method @Nullable public Integer getInMemoryServerConfigsCacheSize();
+ method public int getInMemoryServerConfigsCacheSize();
+ method public boolean hasInMemoryServerConfigsCacheSize();
}
public static final class QuicOptions.Builder {
diff --git a/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java b/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java
new file mode 100644
index 0000000..e66e7ae
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering;
+
+import android.annotation.NonNull;
+import android.os.NativeHandle;
+
+import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
+
+import java.util.ArrayList;
+
+/** Abstraction of Tetheroffload HAL interface */
+interface IOffloadHal {
+ /*
+ * Initialize the Tetheroffload HAL. Offload management process need to know conntrack rules to
+ * support NAT, but it may not have permission to create netlink netfilter sockets. Create two
+ * netlink netfilter sockets and share them with offload management process.
+ */
+ boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2,
+ @NonNull OffloadHalCallback callback);
+
+ /** Stop the Tetheroffload HAL. */
+ boolean stopOffload();
+
+ /** Get HAL interface version number. */
+ int getVersion();
+
+ /** Get Tx/Rx usage from last query. */
+ ForwardedStats getForwardedStats(@NonNull String upstream);
+
+ /** Set local prefixes to offload management process. */
+ boolean setLocalPrefixes(@NonNull ArrayList<String> localPrefixes);
+
+ /** Set data limit value to offload management process. */
+ boolean setDataLimit(@NonNull String iface, long limit);
+
+ /** Set data warning and limit value to offload management process. */
+ boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit);
+
+ /** Set upstream parameters to offload management process. */
+ boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr,
+ @NonNull String v4gateway, @NonNull ArrayList<String> v6gws);
+
+ /** Add downstream prefix to offload management process. */
+ boolean addDownstream(@NonNull String ifname, @NonNull String prefix);
+
+ /** Remove downstream prefix from offload management process. */
+ boolean removeDownstream(@NonNull String ifname, @NonNull String prefix);
+}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index d2f177d..5fa6b2d 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -26,8 +26,8 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -98,9 +98,8 @@
private final OffloadTetheringStatsProvider mStatsProvider;
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
- private boolean mConfigInitialized;
@OffloadHardwareInterface.OffloadHalVersion
- private int mControlHalVersion;
+ private int mOffloadHalVersion;
private LinkProperties mUpstreamLinkProperties;
// The complete set of offload-exempt prefixes passed in via Tethering from
// all upstream and downstream sources.
@@ -205,20 +204,11 @@
return false;
}
- if (!mConfigInitialized) {
- mConfigInitialized = mHwInterface.initOffloadConfig();
- if (!mConfigInitialized) {
- mLog.i("tethering offload config not supported");
- stop();
- return false;
- }
- }
-
- mControlHalVersion = mHwInterface.initOffloadControl(
+ mOffloadHalVersion = mHwInterface.initOffload(
// OffloadHardwareInterface guarantees that these callback
// methods are called on the handler passed to it, which is the
// same as mHandler, as coordinated by the setup in Tethering.
- new OffloadHardwareInterface.ControlCallback() {
+ new OffloadHardwareInterface.OffloadHalCallback() {
@Override
public void onStarted() {
if (!started()) return;
@@ -305,11 +295,11 @@
final boolean isStarted = started();
if (!isStarted) {
- mLog.i("tethering offload control not supported");
+ mLog.i("tethering offload not supported");
stop();
} else {
mLog.log("tethering offload started, version: "
- + OffloadHardwareInterface.halVerToString(mControlHalVersion));
+ + OffloadHardwareInterface.halVerToString(mOffloadHalVersion));
mNatUpdateCallbacksReceived = 0;
mNatUpdateNetlinkErrors = 0;
maybeSchedulePollingStats();
@@ -325,9 +315,8 @@
final boolean wasStarted = started();
updateStatsForCurrentUpstream();
mUpstreamLinkProperties = null;
- mHwInterface.stopOffloadControl();
- mControlHalVersion = OFFLOAD_HAL_VERSION_NONE;
- mConfigInitialized = false;
+ mHwInterface.stopOffload();
+ mOffloadHalVersion = OFFLOAD_HAL_VERSION_NONE;
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
mHandler.removeCallbacks(mScheduledPollingTask);
}
@@ -335,7 +324,7 @@
}
private boolean started() {
- return mConfigInitialized && mControlHalVersion != OFFLOAD_HAL_VERSION_NONE;
+ return mOffloadHalVersion != OFFLOAD_HAL_VERSION_NONE;
}
@VisibleForTesting
@@ -528,7 +517,7 @@
}
private boolean useStatsPolling() {
- return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0;
+ return mOffloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_0;
}
private boolean maybeUpdateDataWarningAndLimit(String iface) {
@@ -540,7 +529,7 @@
final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE);
final boolean ret;
- if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (mOffloadHalVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes);
} else {
ret = mHwInterface.setDataLimit(iface, quota.limitBytes);
@@ -611,7 +600,7 @@
for (RouteInfo ri : oldRoutes) {
if (shouldIgnoreDownstreamRoute(ri)) continue;
if (!newRoutes.contains(ri)) {
- mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
+ mHwInterface.removeDownstream(ifname, ri.getDestination().toString());
}
}
@@ -619,7 +608,7 @@
for (RouteInfo ri : newRoutes) {
if (shouldIgnoreDownstreamRoute(ri)) continue;
if (!oldRoutes.contains(ri)) {
- mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
+ mHwInterface.addDownstream(ifname, ri.getDestination().toString());
}
}
}
@@ -639,7 +628,7 @@
for (RouteInfo route : lp.getRoutes()) {
if (shouldIgnoreDownstreamRoute(route)) continue;
- mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString());
+ mHwInterface.removeDownstream(ifname, route.getDestination().toString());
}
}
@@ -768,7 +757,7 @@
final boolean isStarted = started();
pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
pw.println("Offload Control HAL version: "
- + OffloadHardwareInterface.halVerToString(mControlHalVersion));
+ + OffloadHardwareInterface.halVerToString(mOffloadHalVersion));
LinkProperties lp = mUpstreamLinkProperties;
String upstream = (lp != null) ? lp.getInterfaceName() : null;
pw.println("Current upstream: " + upstream);
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java b/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java
new file mode 100644
index 0000000..e7dc757
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java
@@ -0,0 +1,304 @@
+/*
+ * 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.networkstack.tethering;
+
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL;
+
+import android.annotation.NonNull;
+import android.hardware.tetheroffload.ForwardedStats;
+import android.hardware.tetheroffload.IOffload;
+import android.hardware.tetheroffload.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.NatTimeoutUpdate;
+import android.hardware.tetheroffload.NetworkProtocol;
+import android.hardware.tetheroffload.OffloadCallbackEvent;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
+import android.system.OsConstants;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.SharedLog;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
+
+import java.util.ArrayList;
+
+/**
+ * The implementation of IOffloadHal which based on Stable AIDL interface
+ */
+public class OffloadHalAidlImpl implements IOffloadHal {
+ private static final String TAG = OffloadHalAidlImpl.class.getSimpleName();
+ private static final String HAL_INSTANCE_NAME = IOffload.DESCRIPTOR + "/default";
+
+ private final Handler mHandler;
+ private final SharedLog mLog;
+ private final IOffload mIOffload;
+ @OffloadHardwareInterface.OffloadHalVersion
+ private final int mOffloadVersion;
+
+ private TetheringOffloadCallback mTetheringOffloadCallback;
+
+ public OffloadHalAidlImpl(int version, @NonNull IOffload offload, @NonNull Handler handler,
+ @NonNull SharedLog log) {
+ mOffloadVersion = version;
+ mIOffload = offload;
+ mHandler = handler;
+ mLog = log.forSubComponent(TAG);
+ }
+
+ /**
+ * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the
+ * management process.
+ */
+ public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2,
+ @NonNull OffloadHalCallback callback) {
+ final String methodStr = String.format("initOffload(%d, %d, %s)",
+ handle1.getFileDescriptor().getInt$(), handle2.getFileDescriptor().getInt$(),
+ (callback == null) ? "null"
+ : "0x" + Integer.toHexString(System.identityHashCode(callback)));
+ mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, callback, mLog);
+ try {
+ mIOffload.initOffload(
+ ParcelFileDescriptor.adoptFd(handle1.getFileDescriptor().getInt$()),
+ ParcelFileDescriptor.adoptFd(handle2.getFileDescriptor().getInt$()),
+ mTetheringOffloadCallback);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Stop the Tetheroffload HAL. */
+ public boolean stopOffload() {
+ final String methodStr = "stopOffload()";
+ try {
+ mIOffload.stopOffload();
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+
+ mTetheringOffloadCallback = null;
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Get HAL interface version number. */
+ public int getVersion() {
+ return mOffloadVersion;
+ }
+
+ /** Get Tx/Rx usage from last query. */
+ public OffloadHardwareInterface.ForwardedStats getForwardedStats(@NonNull String upstream) {
+ ForwardedStats stats = new ForwardedStats();
+ final String methodStr = String.format("getForwardedStats(%s)", upstream);
+ try {
+ stats = mIOffload.getForwardedStats(upstream);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ }
+ mLog.i(methodStr);
+ return new OffloadHardwareInterface.ForwardedStats(stats.rxBytes, stats.txBytes);
+ }
+
+ /** Set local prefixes to offload management process. */
+ public boolean setLocalPrefixes(@NonNull ArrayList<String> localPrefixes) {
+ final String methodStr = String.format("setLocalPrefixes([%s])",
+ String.join(",", localPrefixes));
+ try {
+ mIOffload.setLocalPrefixes(localPrefixes.toArray(new String[localPrefixes.size()]));
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /**
+ * Set data limit value to offload management process.
+ * Method setDataLimit is deprecated in AIDL, so call setDataWarningAndLimit instead,
+ * with warningBytes set to its MAX_VALUE.
+ */
+ public boolean setDataLimit(@NonNull String iface, long limit) {
+ final long warning = Long.MAX_VALUE;
+ final String methodStr = String.format("setDataLimit(%s, %d)", iface, limit);
+ try {
+ mIOffload.setDataWarningAndLimit(iface, warning, limit);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Set data warning and limit value to offload management process. */
+ public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) {
+ final String methodStr =
+ String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
+ try {
+ mIOffload.setDataWarningAndLimit(iface, warning, limit);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Set upstream parameters to offload management process. */
+ public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr,
+ @NonNull String v4gateway, @NonNull ArrayList<String> v6gws) {
+ final String methodStr = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
+ iface, v4addr, v4gateway, String.join(",", v6gws));
+ try {
+ mIOffload.setUpstreamParameters(iface, v4addr, v4gateway,
+ v6gws.toArray(new String[v6gws.size()]));
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Add downstream prefix to offload management process. */
+ public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) {
+ final String methodStr = String.format("addDownstream(%s, %s)", ifname, prefix);
+ try {
+ mIOffload.addDownstream(ifname, prefix);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /** Remove downstream prefix from offload management process. */
+ public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) {
+ final String methodStr = String.format("removeDownstream(%s, %s)", ifname, prefix);
+ try {
+ mIOffload.removeDownstream(ifname, prefix);
+ } catch (Exception e) {
+ logAndIgnoreException(e, methodStr);
+ return false;
+ }
+ mLog.i(methodStr);
+ return true;
+ }
+
+ /**
+ * Get {@link IOffloadHal} object from the AIDL service.
+ *
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @param log Log to be used by the repository.
+ */
+ public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) {
+ // Tetheroffload AIDL interface is only supported after U.
+ if (!SdkLevel.isAtLeastU() || !ServiceManager.isDeclared(HAL_INSTANCE_NAME)) return null;
+
+ IOffload offload = IOffload.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME));
+ if (offload == null) return null;
+
+ return new OffloadHalAidlImpl(OFFLOAD_HAL_VERSION_AIDL, offload, handler, log);
+ }
+
+ private void logAndIgnoreException(Exception e, final String methodStr) {
+ mLog.e(methodStr + " failed with " + e.getClass().getSimpleName() + ": ", e);
+ }
+
+ private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
+ public final Handler handler;
+ public final OffloadHalCallback callback;
+ public final SharedLog log;
+
+ TetheringOffloadCallback(
+ Handler h, OffloadHalCallback cb, SharedLog sharedLog) {
+ handler = h;
+ callback = cb;
+ log = sharedLog;
+ }
+
+ private void handleOnEvent(int event) {
+ switch (event) {
+ case OffloadCallbackEvent.OFFLOAD_STARTED:
+ callback.onStarted();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
+ callback.onStoppedError();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
+ callback.onStoppedUnsupported();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
+ callback.onSupportAvailable();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
+ callback.onStoppedLimitReached();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
+ callback.onWarningReached();
+ break;
+ default:
+ log.e("Unsupported OffloadCallbackEvent: " + event);
+ }
+ }
+
+ @Override
+ public void onEvent(int event) {
+ handler.post(() -> {
+ handleOnEvent(event);
+ });
+ }
+
+ @Override
+ public void updateTimeout(NatTimeoutUpdate params) {
+ handler.post(() -> {
+ callback.onNatTimeoutUpdate(
+ networkProtocolToOsConstant(params.proto),
+ params.src.addr, params.src.port,
+ params.dst.addr, params.dst.port);
+ });
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return ITetheringOffloadCallback.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return ITetheringOffloadCallback.VERSION;
+ }
+ }
+
+ private static int networkProtocolToOsConstant(int proto) {
+ switch (proto) {
+ case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
+ case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
+ default:
+ // The caller checks this value and will log an error. Just make
+ // sure it won't collide with valid OsConstants.IPPROTO_* values.
+ return -Math.abs(proto);
+ }
+ }
+}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java
new file mode 100644
index 0000000..3e02543
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java
@@ -0,0 +1,436 @@
+/*
+ * 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.networkstack.tethering;
+
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.halVerToString;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
+
+import android.annotation.NonNull;
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
+import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.net.module.util.SharedLog;
+import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+/**
+ * The implementation of IOffloadHal which based on HIDL interfaces
+ */
+public class OffloadHalHidlImpl implements IOffloadHal {
+ private static final String TAG = OffloadHalHidlImpl.class.getSimpleName();
+ private static final String YIELDS = " -> ";
+
+ private final Handler mHandler;
+ private final SharedLog mLog;
+ private final IOffloadConfig mIOffloadConfig;
+ private final IOffloadControl mIOffloadControl;
+ @OffloadHardwareInterface.OffloadHalVersion
+ private final int mOffloadControlVersion;
+
+ private OffloadHalCallback mOffloadHalCallback;
+ private TetheringOffloadCallback mTetheringOffloadCallback;
+
+ public OffloadHalHidlImpl(int version, @NonNull IOffloadConfig config,
+ @NonNull IOffloadControl control, @NonNull Handler handler, @NonNull SharedLog log) {
+ mOffloadControlVersion = version;
+ mIOffloadConfig = config;
+ mIOffloadControl = control;
+ mHandler = handler;
+ mLog = log.forSubComponent(TAG);
+ }
+
+ /**
+ * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the
+ * management process.
+ */
+ public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2,
+ @NonNull OffloadHalCallback callback) {
+ final String logmsg = String.format("initOffload(%d, %d, %s)",
+ handle1.getFileDescriptor().getInt$(), handle2.getFileDescriptor().getInt$(),
+ (callback == null) ? "null"
+ : "0x" + Integer.toHexString(System.identityHashCode(callback)));
+
+ mOffloadHalCallback = callback;
+ mTetheringOffloadCallback = new TetheringOffloadCallback(
+ mHandler, mOffloadHalCallback, mLog, mOffloadControlVersion);
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadConfig.setHandles(handle1, handle2,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ mIOffloadControl.initOffload(
+ mTetheringOffloadCallback,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Stop the Tetheroffload HAL. */
+ public boolean stopOffload() {
+ try {
+ mIOffloadControl.stopOffload(
+ (boolean success, String errMsg) -> {
+ if (!success) mLog.e("stopOffload failed: " + errMsg);
+ });
+ } catch (RemoteException e) {
+ mLog.e("failed to stopOffload: " + e);
+ }
+ mOffloadHalCallback = null;
+ mTetheringOffloadCallback = null;
+ mLog.log("stopOffload()");
+ return true;
+ }
+
+ /** Get HAL interface version number. */
+ public int getVersion() {
+ return mOffloadControlVersion;
+ }
+
+ /** Get Tx/Rx usage from last query. */
+ public ForwardedStats getForwardedStats(@NonNull String upstream) {
+ final String logmsg = String.format("getForwardedStats(%s)", upstream);
+
+ final ForwardedStats stats = new ForwardedStats();
+ try {
+ mIOffloadControl.getForwardedStats(
+ upstream,
+ (long rxBytes, long txBytes) -> {
+ stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
+ stats.txBytes = (txBytes > 0) ? txBytes : 0;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return stats;
+ }
+
+ return stats;
+ }
+
+ /** Set local prefixes to offload management process. */
+ public boolean setLocalPrefixes(@NonNull ArrayList<String> localPrefixes) {
+ final String logmsg = String.format("setLocalPrefixes([%s])",
+ String.join(",", localPrefixes));
+
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadControl.setLocalPrefixes(localPrefixes,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Set data limit value to offload management process. */
+ public boolean setDataLimit(@NonNull String iface, long limit) {
+
+ final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadControl.setDataLimit(
+ iface, limit,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Set data warning and limit value to offload management process. */
+ public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) {
+ throw new UnsupportedOperationException(
+ "setDataWarningAndLimit is not supported below HAL V1.1");
+ }
+ final String logmsg =
+ String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl)
+ .setDataWarningAndLimit(
+ iface, warning, limit,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Set upstream parameters to offload management process. */
+ public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr,
+ @NonNull String v4gateway, @NonNull ArrayList<String> v6gws) {
+ final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
+ iface, v4addr, v4gateway, String.join(",", v6gws));
+
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadControl.setUpstreamParameters(
+ iface, v4addr, v4gateway, v6gws,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Add downstream prefix to offload management process. */
+ public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) {
+ final String logmsg = String.format("addDownstream(%s, %s)", ifname, prefix);
+
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadControl.addDownstream(ifname, prefix,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /** Remove downstream prefix from offload management process. */
+ public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) {
+ final String logmsg = String.format("removeDownstream(%s, %s)", ifname, prefix);
+
+ final CbResults results = new CbResults();
+ try {
+ mIOffloadControl.removeDownstream(ifname, prefix,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
+ /**
+ * Get {@link IOffloadHal} object from the HIDL service.
+ *
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @param log Log to be used by the repository.
+ */
+ public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) {
+ IOffloadConfig config = null;
+ try {
+ config = IOffloadConfig.getService(true /*retry*/);
+ } catch (RemoteException | NoSuchElementException e) {
+ log.e("getIOffloadConfig error " + e);
+ return null;
+ }
+
+ IOffloadControl control = null;
+ int version = OFFLOAD_HAL_VERSION_NONE;
+ try {
+ control = android.hardware.tetheroffload.control
+ .V1_1.IOffloadControl.getService(true /*retry*/);
+ version = OFFLOAD_HAL_VERSION_HIDL_1_1;
+ } catch (NoSuchElementException e) {
+ // Unsupported by device.
+ } catch (RemoteException e) {
+ log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_1);
+ }
+ if (control == null) {
+ try {
+ control = IOffloadControl.getService(true /*retry*/);
+ version = OFFLOAD_HAL_VERSION_HIDL_1_0;
+ } catch (NoSuchElementException e) {
+ // Unsupported by device.
+ } catch (RemoteException e) {
+ log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_0);
+ }
+ }
+
+ if (config == null || control == null) return null;
+
+ return new OffloadHalHidlImpl(version, config, control, handler, log);
+ }
+
+ private void record(String msg, Throwable t) {
+ mLog.e(msg + YIELDS + "exception: " + t);
+ }
+
+ private void record(String msg, CbResults results) {
+ final String logmsg = msg + YIELDS + results;
+ if (!results.mSuccess) {
+ mLog.e(logmsg);
+ } else {
+ mLog.log(logmsg);
+ }
+ }
+
+ private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
+ public final Handler handler;
+ public final OffloadHalCallback callback;
+ public final SharedLog log;
+ private final int mOffloadControlVersion;
+
+ TetheringOffloadCallback(
+ Handler h, OffloadHalCallback cb, SharedLog sharedLog, int offloadControlVersion) {
+ handler = h;
+ callback = cb;
+ log = sharedLog;
+ this.mOffloadControlVersion = offloadControlVersion;
+ }
+
+ private void handleOnEvent(int event) {
+ switch (event) {
+ case OffloadCallbackEvent.OFFLOAD_STARTED:
+ callback.onStarted();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
+ callback.onStoppedError();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
+ callback.onStoppedUnsupported();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
+ callback.onSupportAvailable();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
+ callback.onStoppedLimitReached();
+ break;
+ case android.hardware.tetheroffload.control
+ .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
+ callback.onWarningReached();
+ break;
+ default:
+ log.e("Unsupported OffloadCallbackEvent: " + event);
+ }
+ }
+
+ @Override
+ public void onEvent(int event) {
+ // The implementation should never call onEvent()) if the event is already reported
+ // through newer callback.
+ if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_HIDL_1_0) {
+ Log.wtf(TAG, "onEvent(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ }
+ handler.post(() -> {
+ handleOnEvent(event);
+ });
+ }
+
+ @Override
+ public void onEvent_1_1(int event) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) {
+ Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ return;
+ }
+ handler.post(() -> {
+ handleOnEvent(event);
+ });
+ }
+
+ @Override
+ public void updateTimeout(NatTimeoutUpdate params) {
+ handler.post(() -> {
+ callback.onNatTimeoutUpdate(
+ networkProtocolToOsConstant(params.proto),
+ params.src.addr, uint16(params.src.port),
+ params.dst.addr, uint16(params.dst.port));
+ });
+ }
+ }
+
+ private static int networkProtocolToOsConstant(int proto) {
+ switch (proto) {
+ case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
+ case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
+ default:
+ // The caller checks this value and will log an error. Just make
+ // sure it won't collide with valid OsConstants.IPPROTO_* values.
+ return -Math.abs(proto);
+ }
+ }
+
+ private static class CbResults {
+ boolean mSuccess;
+ String mErrMsg;
+
+ @Override
+ public String toString() {
+ if (mSuccess) {
+ return "ok";
+ } else {
+ return "fail: " + mErrMsg;
+ }
+ }
+ }
+}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index 76ddfe5..ea20063 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -18,25 +18,15 @@
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
-import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
-import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
-import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.NativeHandle;
-import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.util.Log;
-import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.SharedLog;
@@ -54,8 +44,6 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
-import java.util.NoSuchElementException;
-
/**
* Capture tethering dependencies, for injection.
@@ -86,43 +74,43 @@
private final Handler mHandler;
private final SharedLog mLog;
private final Dependencies mDeps;
- private IOffloadControl mOffloadControl;
+ private IOffloadHal mIOffload;
// TODO: Use major-minor version control to prevent from defining new constants.
static final int OFFLOAD_HAL_VERSION_NONE = 0;
- static final int OFFLOAD_HAL_VERSION_1_0 = 1;
- static final int OFFLOAD_HAL_VERSION_1_1 = 2;
+ static final int OFFLOAD_HAL_VERSION_HIDL_1_0 = 1;
+ static final int OFFLOAD_HAL_VERSION_HIDL_1_1 = 2;
+ static final int OFFLOAD_HAL_VERSION_AIDL = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "OFFLOAD_HAL_VERSION_", value = {
OFFLOAD_HAL_VERSION_NONE,
- OFFLOAD_HAL_VERSION_1_0,
- OFFLOAD_HAL_VERSION_1_1
+ OFFLOAD_HAL_VERSION_HIDL_1_0,
+ OFFLOAD_HAL_VERSION_HIDL_1_1,
+ OFFLOAD_HAL_VERSION_AIDL,
})
public @interface OffloadHalVersion {}
- @OffloadHalVersion
- private int mOffloadControlVersion = OFFLOAD_HAL_VERSION_NONE;
@NonNull
static String halVerToString(int version) {
switch(version) {
- case OFFLOAD_HAL_VERSION_1_0:
- return "1.0";
- case OFFLOAD_HAL_VERSION_1_1:
- return "1.1";
+ case OFFLOAD_HAL_VERSION_HIDL_1_0:
+ return "HIDL 1.0";
+ case OFFLOAD_HAL_VERSION_HIDL_1_1:
+ return "HIDL 1.1";
+ case OFFLOAD_HAL_VERSION_AIDL:
+ return "AIDL";
case OFFLOAD_HAL_VERSION_NONE:
return "None";
default:
throw new IllegalArgumentException("Unsupported version int " + version);
}
-
}
- private TetheringOffloadCallback mTetheringOffloadCallback;
- private ControlCallback mControlCallback;
+ private OffloadHalCallback mOffloadHalCallback;
/** The callback to notify status of offload management process. */
- public static class ControlCallback {
+ public static class OffloadHalCallback {
/** Offload started. */
public void onStarted() {}
/**
@@ -179,7 +167,7 @@
}
public OffloadHardwareInterface(Handler h, SharedLog log) {
- this(h, log, new Dependencies(log));
+ this(h, log, new Dependencies(h, log));
}
OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
@@ -190,45 +178,21 @@
/** Capture OffloadHardwareInterface dependencies, for injection. */
static class Dependencies {
+ private final Handler mHandler;
private final SharedLog mLog;
- Dependencies(SharedLog log) {
+ Dependencies(Handler handler, SharedLog log) {
+ mHandler = handler;
mLog = log;
}
- public IOffloadConfig getOffloadConfig() {
- try {
- return IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("getIOffloadConfig error " + e);
- return null;
- }
- }
-
- @NonNull
- public Pair<IOffloadControl, Integer> getOffloadControl() {
- IOffloadControl hal = null;
- int version = OFFLOAD_HAL_VERSION_NONE;
- try {
- hal = android.hardware.tetheroffload.control
- .V1_1.IOffloadControl.getService(true /*retry*/);
- version = OFFLOAD_HAL_VERSION_1_1;
- } catch (NoSuchElementException e) {
- // Unsupported by device.
- } catch (RemoteException e) {
- mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_1);
- }
+ public IOffloadHal getOffload() {
+ // Prefer AIDL implementation if its service is declared.
+ IOffloadHal hal = OffloadHalAidlImpl.getIOffloadHal(mHandler, mLog);
if (hal == null) {
- try {
- hal = IOffloadControl.getService(true /*retry*/);
- version = OFFLOAD_HAL_VERSION_1_0;
- } catch (NoSuchElementException e) {
- // Unsupported by device.
- } catch (RemoteException e) {
- mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_0);
- }
+ hal = OffloadHalHidlImpl.getIOffloadHal(mHandler, mLog);
}
- return new Pair<IOffloadControl, Integer>(hal, version);
+ return hal;
}
public NativeHandle createConntrackSocket(final int groups) {
@@ -273,56 +237,6 @@
return DEFAULT_TETHER_OFFLOAD_DISABLED;
}
- /**
- * Offload management process need to know conntrack rules to support NAT, but it may not have
- * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
- * share them with offload management process.
- */
- public boolean initOffloadConfig() {
- final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
- if (offloadConfig == null) {
- mLog.e("Could not find IOffloadConfig service");
- return false;
- }
- // Per the IConfigOffload definition:
- //
- // h1 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
- //
- // h2 provides a file descriptor bound to the following netlink groups
- // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
- final NativeHandle h1 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
- if (h1 == null) return false;
-
- requestSocketDump(h1);
-
- final NativeHandle h2 = mDeps.createConntrackSocket(
- NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
- if (h2 == null) {
- closeFdInNativeHandle(h1);
- return false;
- }
-
- final CbResults results = new CbResults();
- try {
- offloadConfig.setHandles(h1, h2,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record("initOffloadConfig, setHandles fail", e);
- return false;
- }
- // Explicitly close FDs.
- closeFdInNativeHandle(h1);
- closeFdInNativeHandle(h2);
-
- record("initOffloadConfig, setHandles results:", results);
- return results.mSuccess;
- }
-
@VisibleForTesting
void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) {
final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
@@ -355,165 +269,107 @@
(short) (NLM_F_REQUEST | NLM_F_DUMP));
}
- private void closeFdInNativeHandle(final NativeHandle h) {
- try {
- h.close();
- } catch (IOException | IllegalStateException e) {
- // IllegalStateException means fd is already closed, do nothing here.
- // Also nothing we can do if IOException.
+ private void maybeCloseFdInNativeHandles(final NativeHandle... handles) {
+ for (NativeHandle h : handles) {
+ if (h == null) continue;
+ try {
+ h.close();
+ } catch (IOException | IllegalStateException e) {
+ // IllegalStateException means fd is already closed, do nothing here.
+ // Also nothing we can do if IOException.
+ }
}
}
+ private int initWithHandles(NativeHandle h1, NativeHandle h2) {
+ if (h1 == null || h2 == null) {
+ mLog.e("Failed to create socket.");
+ return OFFLOAD_HAL_VERSION_NONE;
+ }
+
+ requestSocketDump(h1);
+ if (!mIOffload.initOffload(h1, h2, mOffloadHalCallback)) {
+ mIOffload.stopOffload();
+ mLog.e("Failed to initialize offload.");
+ return OFFLOAD_HAL_VERSION_NONE;
+ }
+
+ return mIOffload.getVersion();
+ }
+
/**
* Initialize the tethering offload HAL.
*
* @return one of {@code OFFLOAD_HAL_VERSION_*} represents the HAL version, or
* {@link #OFFLOAD_HAL_VERSION_NONE} if failed.
*/
- public int initOffloadControl(ControlCallback controlCb) {
- mControlCallback = controlCb;
-
- if (mOffloadControl == null) {
- final Pair<IOffloadControl, Integer> halAndVersion = mDeps.getOffloadControl();
- mOffloadControl = halAndVersion.first;
- mOffloadControlVersion = halAndVersion.second;
- if (mOffloadControl == null) {
- mLog.e("tethering IOffloadControl.getService() returned null");
+ public int initOffload(OffloadHalCallback offloadCb) {
+ if (mIOffload == null) {
+ mIOffload = mDeps.getOffload();
+ if (mIOffload == null) {
+ mLog.e("No tethering offload HAL service found.");
return OFFLOAD_HAL_VERSION_NONE;
}
- mLog.i("tethering offload control version "
- + halVerToString(mOffloadControlVersion) + " is supported.");
+ mLog.i("Tethering offload version "
+ + halVerToString(mIOffload.getVersion()) + " is supported.");
}
- final String logmsg = String.format("initOffloadControl(%s)",
- (controlCb == null) ? "null"
- : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
+ // Per the IOffload definition:
+ //
+ // h1 provides a file descriptor bound to the following netlink groups
+ // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+ //
+ // h2 provides a file descriptor bound to the following netlink groups
+ // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+ final NativeHandle h1 = mDeps.createConntrackSocket(
+ NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
+ final NativeHandle h2 = mDeps.createConntrackSocket(
+ NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
- mTetheringOffloadCallback = new TetheringOffloadCallback(
- mHandler, mControlCallback, mLog, mOffloadControlVersion);
- final CbResults results = new CbResults();
- try {
- mOffloadControl.initOffload(
- mTetheringOffloadCallback,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return OFFLOAD_HAL_VERSION_NONE;
+ mOffloadHalCallback = offloadCb;
+ final int version = initWithHandles(h1, h2);
+
+ // Explicitly close FDs for HIDL. AIDL will pass the original FDs to the service,
+ // they shouldn't be closed here.
+ if (version < OFFLOAD_HAL_VERSION_AIDL) {
+ maybeCloseFdInNativeHandles(h1, h2);
}
-
- record(logmsg, results);
- return results.mSuccess ? mOffloadControlVersion : OFFLOAD_HAL_VERSION_NONE;
+ return version;
}
- /** Stop IOffloadControl. */
- public void stopOffloadControl() {
- if (mOffloadControl != null) {
- try {
- mOffloadControl.stopOffload(
- (boolean success, String errMsg) -> {
- if (!success) mLog.e("stopOffload failed: " + errMsg);
- });
- } catch (RemoteException e) {
- mLog.e("failed to stopOffload: " + e);
+ /** Stop the tethering offload HAL. */
+ public void stopOffload() {
+ if (mIOffload != null) {
+ if (!mIOffload.stopOffload()) {
+ mLog.e("Failed to stop offload.");
}
}
- mOffloadControl = null;
- mTetheringOffloadCallback = null;
- mControlCallback = null;
- mLog.log("stopOffloadControl()");
+ mIOffload = null;
+ mOffloadHalCallback = null;
}
/** Get Tx/Rx usage from last query. */
public ForwardedStats getForwardedStats(String upstream) {
- final String logmsg = String.format("getForwardedStats(%s)", upstream);
-
- final ForwardedStats stats = new ForwardedStats();
- try {
- mOffloadControl.getForwardedStats(
- upstream,
- (long rxBytes, long txBytes) -> {
- stats.rxBytes = (rxBytes > 0) ? rxBytes : 0;
- stats.txBytes = (txBytes > 0) ? txBytes : 0;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return stats;
- }
-
- return stats;
+ return mIOffload.getForwardedStats(upstream);
}
/** Set local prefixes to offload management process. */
public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
- final String logmsg = String.format("setLocalPrefixes([%s])",
- String.join(",", localPrefixes));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setLocalPrefixes(localPrefixes,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
+ return mIOffload.setLocalPrefixes(localPrefixes);
}
/** Set data limit value to offload management process. */
public boolean setDataLimit(String iface, long limit) {
-
- final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setDataLimit(
- iface, limit,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
+ return mIOffload.setDataLimit(iface, limit);
}
/** Set data warning and limit value to offload management process. */
public boolean setDataWarningAndLimit(String iface, long warning, long limit) {
- if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
- throw new IllegalArgumentException(
+ if (mIOffload.getVersion() < OFFLOAD_HAL_VERSION_HIDL_1_1) {
+ throw new UnsupportedOperationException(
"setDataWarningAndLimit is not supported below HAL V1.1");
}
- final String logmsg =
- String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
-
- final CbResults results = new CbResults();
- try {
- ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mOffloadControl)
- .setDataWarningAndLimit(
- iface, warning, limit,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
+ return mIOffload.setDataWarningAndLimit(iface, warning, limit);
}
/** Set upstream parameters to offload management process. */
@@ -523,178 +379,16 @@
v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
-
- final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
- iface, v4addr, v4gateway, String.join(",", v6gws));
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.setUpstreamParameters(
- iface, v4addr, v4gateway, v6gws,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
+ return mIOffload.setUpstreamParameters(iface, v4addr, v4gateway, v6gws);
}
/** Add downstream prefix to offload management process. */
- public boolean addDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.addDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
+ public boolean addDownstream(String ifname, String prefix) {
+ return mIOffload.addDownstream(ifname, prefix);
}
/** Remove downstream prefix from offload management process. */
- public boolean removeDownstreamPrefix(String ifname, String prefix) {
- final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix);
-
- final CbResults results = new CbResults();
- try {
- mOffloadControl.removeDownstream(ifname, prefix,
- (boolean success, String errMsg) -> {
- results.mSuccess = success;
- results.mErrMsg = errMsg;
- });
- } catch (RemoteException e) {
- record(logmsg, e);
- return false;
- }
-
- record(logmsg, results);
- return results.mSuccess;
- }
-
- private void record(String msg, Throwable t) {
- mLog.e(msg + YIELDS + "exception: " + t);
- }
-
- private void record(String msg, CbResults results) {
- final String logmsg = msg + YIELDS + results;
- if (!results.mSuccess) {
- mLog.e(logmsg);
- } else {
- mLog.log(logmsg);
- }
- }
-
- private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
- public final Handler handler;
- public final ControlCallback controlCb;
- public final SharedLog log;
- private final int mOffloadControlVersion;
-
- TetheringOffloadCallback(
- Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion) {
- handler = h;
- controlCb = cb;
- log = sharedLog;
- this.mOffloadControlVersion = offloadControlVersion;
- }
-
- private void handleOnEvent(int event) {
- switch (event) {
- case OffloadCallbackEvent.OFFLOAD_STARTED:
- controlCb.onStarted();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
- controlCb.onStoppedError();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
- controlCb.onStoppedUnsupported();
- break;
- case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
- controlCb.onSupportAvailable();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
- controlCb.onStoppedLimitReached();
- break;
- case android.hardware.tetheroffload.control
- .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
- controlCb.onWarningReached();
- break;
- default:
- log.e("Unsupported OffloadCallbackEvent: " + event);
- }
- }
-
- @Override
- public void onEvent(int event) {
- // The implementation should never call onEvent()) if the event is already reported
- // through newer callback.
- if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_1_0) {
- Log.wtf(TAG, "onEvent(" + event + ") fired on HAL "
- + halVerToString(mOffloadControlVersion));
- }
- handler.post(() -> {
- handleOnEvent(event);
- });
- }
-
- @Override
- public void onEvent_1_1(int event) {
- if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
- Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL "
- + halVerToString(mOffloadControlVersion));
- return;
- }
- handler.post(() -> {
- handleOnEvent(event);
- });
- }
-
- @Override
- public void updateTimeout(NatTimeoutUpdate params) {
- handler.post(() -> {
- controlCb.onNatTimeoutUpdate(
- networkProtocolToOsConstant(params.proto),
- params.src.addr, uint16(params.src.port),
- params.dst.addr, uint16(params.dst.port));
- });
- }
- }
-
- private static int networkProtocolToOsConstant(int proto) {
- switch (proto) {
- case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
- case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
- default:
- // The caller checks this value and will log an error. Just make
- // sure it won't collide with valid OsContants.IPPROTO_* values.
- return -Math.abs(proto);
- }
- }
-
- private static class CbResults {
- boolean mSuccess;
- String mErrMsg;
-
- @Override
- public String toString() {
- if (mSuccess) {
- return "ok";
- } else {
- return "fail: " + mErrMsg;
- }
- }
+ public boolean removeDownstream(String ifname, String prefix) {
+ return mIOffload.removeDownstream(ifname, prefix);
}
}
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 706df4e..b3fb3e4 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -80,7 +80,7 @@
// Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
if (Looper.myLooper() == null) Looper.prepare();
- mDeps = new OffloadHardwareInterface.Dependencies(mLog);
+ mDeps = new OffloadHardwareInterface.Dependencies(mHandler, mLog);
mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps);
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index faca1c8..36c15a7 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -31,8 +31,8 @@
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.testutils.MiscAsserts.assertContainsAll;
import static com.android.testutils.MiscAsserts.assertThrows;
@@ -79,6 +79,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.SharedLog;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
@@ -125,8 +126,8 @@
private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
- ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
+ private final ArgumentCaptor<OffloadHalCallback> mOffloadHalCallbackCaptor =
+ ArgumentCaptor.forClass(OffloadHalCallback.class);
private MockContentResolver mContentResolver;
private final TestLooper mTestLooper = new TestLooper();
private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() {
@@ -151,10 +152,9 @@
FakeSettingsProvider.clearSettingsProvider();
}
- private void setupFunctioningHardwareInterface(int controlVersion) {
- when(mHardware.initOffloadConfig()).thenReturn(true);
- when(mHardware.initOffloadControl(mControlCallbackCaptor.capture()))
- .thenReturn(controlVersion);
+ private void setupFunctioningHardwareInterface(int offloadHalVersion) {
+ when(mHardware.initOffload(mOffloadHalCallbackCaptor.capture()))
+ .thenReturn(offloadHalVersion);
when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
@@ -192,9 +192,9 @@
@Test
public void testStartStop() throws Exception {
stopOffloadController(
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/));
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/));
stopOffloadController(
- startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/));
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_1, true /*expectStart*/));
}
@NonNull
@@ -206,9 +206,8 @@
final InOrder inOrder = inOrder(mHardware);
inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled();
- inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffloadConfig();
- inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffloadControl(
- any(OffloadHardwareInterface.ControlCallback.class));
+ inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffload(
+ any(OffloadHalCallback.class));
inOrder.verifyNoMoreInteractions();
// Clear counters only instead of whole mock to preserve the mocking setup.
clearInvocations(mHardware);
@@ -218,7 +217,7 @@
private void stopOffloadController(final OffloadController offload) throws Exception {
final InOrder inOrder = inOrder(mHardware);
offload.stop();
- inOrder.verify(mHardware, times(1)).stopOffloadControl();
+ inOrder.verify(mHardware, times(1)).stopOffload();
inOrder.verifyNoMoreInteractions();
reset(mHardware);
}
@@ -228,7 +227,7 @@
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
assertThrows(SettingNotFoundException.class, () ->
Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, false /*expectStart*/);
}
@Test
@@ -236,26 +235,26 @@
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
assertThrows(SettingNotFoundException.class, () ->
Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
}
@Test
public void testSettingsAllowsStart() throws Exception {
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
}
@Test
public void testSettingsDisablesStart() throws Exception {
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1);
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, false /*expectStart*/);
}
@Test
public void testSetUpstreamLinkPropertiesWorking() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
// In reality, the UpstreamNetworkMonitor would have passed down to us
// a covering set of local prefixes representing a minimum essential
@@ -426,7 +425,7 @@
public void testGetForwardedStats() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
@@ -521,11 +520,11 @@
// Verify the OffloadController is called by R framework, where the framework doesn't send
// warning.
// R only uses HAL 1.0.
- checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_0);
+ checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_HIDL_1_0);
// Verify the OffloadController is called by S+ framework, where the framework sends
// warning along with limit.
- checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_0);
- checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_1);
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_HIDL_1_0);
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_HIDL_1_1);
}
private void checkSetDataWarningAndLimit(boolean isProviderSetWarning, int controlVersion)
@@ -550,7 +549,7 @@
when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
offload.setUpstreamLinkProperties(lp);
// Applying an interface sends the initial quota to the hardware.
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
Long.MAX_VALUE);
} else {
@@ -576,7 +575,7 @@
mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
}
waitForIdle();
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
ethernetLimit);
} else {
@@ -591,7 +590,7 @@
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
}
waitForIdle();
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
anyLong());
} else {
@@ -603,7 +602,7 @@
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
waitForIdle();
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface,
isProviderSetWarning ? mobileWarning : Long.MAX_VALUE,
mobileLimit);
@@ -620,7 +619,7 @@
mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED);
}
waitForIdle();
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, Long.MAX_VALUE,
Long.MAX_VALUE);
} else {
@@ -655,15 +654,15 @@
}
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
- inOrder.verify(mHardware).stopOffloadControl();
+ inOrder.verify(mHardware).stopOffload();
}
@Test
public void testDataWarningAndLimitCallback_LimitReached() throws Exception {
enableOffload();
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
- final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ final OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue();
callback.onStoppedLimitReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated();
@@ -679,8 +678,8 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.R) // HAL 1.1 is only supported from S
public void testDataWarningAndLimitCallback_WarningReached() throws Exception {
- startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/);
- final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_1, true /*expectStart*/);
+ final OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue();
callback.onWarningReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated();
@@ -695,7 +694,7 @@
public void testAddRemoveDownstreams() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
final InOrder inOrder = inOrder(mHardware);
// Tethering makes several calls to setLocalPrefixes() before add/remove
@@ -710,14 +709,14 @@
usbLinkProperties.addRoute(
new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX);
+ inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, USB_PREFIX);
inOrder.verifyNoMoreInteractions();
// [2] Routes for IPv6 link-local prefixes should never be added.
usbLinkProperties.addRoute(
new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
+ inOrder.verify(mHardware, never()).addDownstream(eq(RNDIS0), anyString());
inOrder.verifyNoMoreInteractions();
// [3] Add an IPv6 prefix for good measure. Only new offload-able
@@ -726,14 +725,14 @@
usbLinkProperties.addRoute(
new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
+ inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, IPV6_DOC_PREFIX);
inOrder.verifyNoMoreInteractions();
// [4] Adding addresses doesn't affect notifyDownstreamLinkProperties().
// The address is passed in by a separate setLocalPrefixes() invocation.
usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64"));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
+ inOrder.verify(mHardware, never()).addDownstream(eq(RNDIS0), anyString());
// [5] Differences in local routes are converted into addDownstream()
// and removeDownstream() invocations accordingly.
@@ -742,8 +741,8 @@
usbLinkProperties.addRoute(
new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST));
offload.notifyDownstreamLinkProperties(usbLinkProperties);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
- inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
+ inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, IPV6_DOC_PREFIX);
+ inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, IPV6_DISCARD_PREFIX);
inOrder.verifyNoMoreInteractions();
// [6] Removing a downstream interface which was never added causes no
@@ -753,8 +752,8 @@
// [7] Removing an active downstream removes all remaining prefixes.
offload.removeDownstreamInterface(RNDIS0);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX);
- inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
+ inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, USB_PREFIX);
+ inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, IPV6_DISCARD_PREFIX);
inOrder.verifyNoMoreInteractions();
}
@@ -762,7 +761,7 @@
public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
@@ -776,7 +775,7 @@
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue();
callback.onStoppedUnsupported();
// Verify forwarded stats behaviour.
@@ -793,7 +792,7 @@
throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
@@ -840,7 +839,7 @@
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue();
callback.onSupportAvailable();
// Verify forwarded stats behaviour.
@@ -859,8 +858,8 @@
// into OffloadController proper. After this, also check for:
// "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128"
"127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64");
- verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24");
- verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64");
+ verify(mHardware, times(1)).addDownstream(WLAN0, "192.168.43.0/24");
+ verify(mHardware, times(1)).addDownstream(WLAN0, "2001:2::/64");
verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
verifyNoMoreInteractions(mHardware);
@@ -871,7 +870,7 @@
enableOffload();
setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/);
// Initialize with fake eth upstream.
final String ethernetIface = "eth1";
@@ -925,7 +924,7 @@
offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
mTetherStatsProvider.onSetAlert(0);
waitForIdle();
- if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) {
mTetherStatsProviderCb.assertNoCallback();
} else {
mTetherStatsProviderCb.expectNotifyAlertReached();
@@ -935,7 +934,7 @@
@Test
public void testSoftwarePollingUsed() throws Exception {
- checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_0);
- checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_1);
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_HIDL_1_1);
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java
new file mode 100644
index 0000000..c9ce64f
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java
@@ -0,0 +1,390 @@
+/*
+ * 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.networkstack.tethering;
+
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.tetheroffload.ForwardedStats;
+import android.hardware.tetheroffload.IOffload;
+import android.hardware.tetheroffload.IPv4AddrPortPair;
+import android.hardware.tetheroffload.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.NatTimeoutUpdate;
+import android.hardware.tetheroffload.NetworkProtocol;
+import android.hardware.tetheroffload.OffloadCallbackEvent;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceSpecificException;
+import android.os.test.TestLooper;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.SharedLog;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class OffloadHalAidlImplTest {
+ private static final String RMNET0 = "test_rmnet_data0";
+
+ private final SharedLog mLog = new SharedLog("test");
+ private final TestLooper mTestLooper = new TestLooper();
+
+ private IOffload mIOffloadMock;
+ private OffloadHalAidlImpl mIOffloadHal;
+ private ITetheringOffloadCallback mTetheringOffloadCallback;
+ private OffloadHalCallback mOffloadHalCallback;
+
+ private void initAndValidateOffloadHal(boolean initSuccess)
+ throws Exception {
+ final FileDescriptor fd1 = new FileDescriptor();
+ final FileDescriptor fd2 = new FileDescriptor();
+ final NativeHandle handle1 = new NativeHandle(fd1, true);
+ final NativeHandle handle2 = new NativeHandle(fd2, true);
+ final ArgumentCaptor<ParcelFileDescriptor> fdCaptor1 =
+ ArgumentCaptor.forClass(ParcelFileDescriptor.class);
+ final ArgumentCaptor<ParcelFileDescriptor> fdCaptor2 =
+ ArgumentCaptor.forClass(ParcelFileDescriptor.class);
+ final ArgumentCaptor<ITetheringOffloadCallback> offloadCallbackCaptor =
+ ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
+ if (initSuccess) {
+ doNothing().when(mIOffloadMock).initOffload(any(), any(), any());
+ } else {
+ doThrow(new IllegalStateException()).when(mIOffloadMock).initOffload(any(), any(),
+ any());
+ }
+ assertEquals(mIOffloadHal.initOffload(handle1, handle2, mOffloadHalCallback),
+ initSuccess);
+ verify(mIOffloadMock).initOffload(fdCaptor1.capture(), fdCaptor2.capture(),
+ offloadCallbackCaptor.capture());
+ assertEquals(fdCaptor1.getValue().getFd(), fd1.getInt$());
+ assertEquals(fdCaptor2.getValue().getFd(), fd2.getInt$());
+ mTetheringOffloadCallback = offloadCallbackCaptor.getValue();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mIOffloadMock = mock(IOffload.class);
+ mIOffloadHal = new OffloadHalAidlImpl(OFFLOAD_HAL_VERSION_AIDL, mIOffloadMock,
+ new Handler(mTestLooper.getLooper()), mLog);
+ mOffloadHalCallback = spy(new OffloadHalCallback());
+ }
+
+ @Test
+ public void testInitOffloadSuccess() throws Exception {
+ initAndValidateOffloadHal(true /* initSuccess */);
+ }
+
+ @Test
+ public void testInitOffloadFailure() throws Exception {
+ initAndValidateOffloadHal(false /* initSuccess */);
+ }
+
+ @Test
+ public void testStopOffloadSuccess() throws Exception {
+ initAndValidateOffloadHal(true);
+ doNothing().when(mIOffloadMock).stopOffload();
+ assertTrue(mIOffloadHal.stopOffload());
+ verify(mIOffloadMock).stopOffload();
+ }
+
+ @Test
+ public void testStopOffloadFailure() throws Exception {
+ initAndValidateOffloadHal(true);
+ doThrow(new IllegalStateException()).when(mIOffloadMock).stopOffload();
+ assertFalse(mIOffloadHal.stopOffload());
+ }
+
+ private void doTestGetForwardedStats(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final ForwardedStats returnStats = new ForwardedStats();
+ if (expectSuccess) {
+ returnStats.rxBytes = 12345;
+ returnStats.txBytes = 67890;
+ when(mIOffloadMock.getForwardedStats(anyString())).thenReturn(returnStats);
+ } else {
+ when(mIOffloadMock.getForwardedStats(anyString()))
+ .thenThrow(new ServiceSpecificException(IOffload.ERROR_CODE_UNUSED));
+ }
+ final OffloadHardwareInterface.ForwardedStats stats =
+ mIOffloadHal.getForwardedStats(RMNET0);
+ verify(mIOffloadMock).getForwardedStats(eq(RMNET0));
+ assertNotNull(stats);
+ assertEquals(stats.rxBytes, returnStats.rxBytes);
+ assertEquals(stats.txBytes, returnStats.txBytes);
+ }
+
+ @Test
+ public void testGetForwardedStatsSuccess() throws Exception {
+ doTestGetForwardedStats(true);
+ }
+
+ @Test
+ public void testGetForwardedStatsFailure() throws Exception {
+ doTestGetForwardedStats(false);
+ }
+
+ private void doTestSetLocalPrefixes(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final ArrayList<String> localPrefixes = new ArrayList<>();
+ localPrefixes.add("127.0.0.0/8");
+ localPrefixes.add("fe80::/64");
+ final String[] localPrefixesArray =
+ localPrefixes.toArray(new String[localPrefixes.size()]);
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).setLocalPrefixes(any());
+ } else {
+ doThrow(new IllegalArgumentException()).when(mIOffloadMock).setLocalPrefixes(any());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.setLocalPrefixes(localPrefixes));
+ verify(mIOffloadMock).setLocalPrefixes(eq(localPrefixesArray));
+ }
+
+ @Test
+ public void testSetLocalPrefixesSuccess() throws Exception {
+ doTestSetLocalPrefixes(true);
+ }
+
+ @Test
+ public void testSetLocalPrefixesFailure() throws Exception {
+ doTestSetLocalPrefixes(false);
+ }
+
+ private void doTestSetDataLimit(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final long limit = 12345;
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
+ } else {
+ doThrow(new IllegalArgumentException())
+ .when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), anyLong());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.setDataLimit(RMNET0, limit));
+ verify(mIOffloadMock).setDataWarningAndLimit(eq(RMNET0), eq(Long.MAX_VALUE), eq(limit));
+ }
+
+ @Test
+ public void testSetDataLimitSuccess() throws Exception {
+ doTestSetDataLimit(true);
+ }
+
+ @Test
+ public void testSetDataLimitFailure() throws Exception {
+ doTestSetDataLimit(false);
+ }
+
+ private void doTestSetDataWarningAndLimit(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final long warning = 12345;
+ final long limit = 67890;
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
+ } else {
+ doThrow(new IllegalArgumentException())
+ .when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), anyLong());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit));
+ verify(mIOffloadMock).setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit));
+ }
+
+ @Test
+ public void testSetDataWarningAndLimitSuccess() throws Exception {
+ doTestSetDataWarningAndLimit(true);
+ }
+
+ @Test
+ public void testSetDataWarningAndLimitFailure() throws Exception {
+ doTestSetDataWarningAndLimit(false);
+ }
+
+ private void doTestSetUpstreamParameters(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final String v4addr = "192.168.10.1";
+ final String v4gateway = "192.168.10.255";
+ final ArrayList<String> v6gws = new ArrayList<>(0);
+ v6gws.add("2001:db8::1");
+ String[] v6gwsArray = v6gws.toArray(new String[v6gws.size()]);
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).setUpstreamParameters(anyString(), anyString(),
+ anyString(), any());
+ } else {
+ doThrow(new IllegalArgumentException()).when(mIOffloadMock).setUpstreamParameters(
+ anyString(), anyString(), anyString(), any());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.setUpstreamParameters(RMNET0, v4addr, v4gateway,
+ v6gws));
+ verify(mIOffloadMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+ eq(v6gwsArray));
+ }
+
+ @Test
+ public void testSetUpstreamParametersSuccess() throws Exception {
+ doTestSetUpstreamParameters(true);
+ }
+
+ @Test
+ public void testSetUpstreamParametersFailure() throws Exception {
+ doTestSetUpstreamParameters(false);
+ }
+
+ private void doTestAddDownstream(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).addDownstream(anyString(), anyString());
+ } else {
+ doThrow(new IllegalStateException()).when(mIOffloadMock).addDownstream(anyString(),
+ anyString());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.addDownstream(ifName, prefix));
+ verify(mIOffloadMock).addDownstream(eq(ifName), eq(prefix));
+ }
+
+ @Test
+ public void testAddDownstreamSuccess() throws Exception {
+ doTestAddDownstream(true);
+ }
+
+ @Test
+ public void testAddDownstreamFailure() throws Exception {
+ doTestAddDownstream(false);
+ }
+
+ private void doTestRemoveDownstream(boolean expectSuccess) throws Exception {
+ initAndValidateOffloadHal(true);
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ if (expectSuccess) {
+ doNothing().when(mIOffloadMock).removeDownstream(anyString(), anyString());
+ } else {
+ doThrow(new IllegalArgumentException()).when(mIOffloadMock).removeDownstream(
+ anyString(), anyString());
+ }
+ assertEquals(expectSuccess, mIOffloadHal.removeDownstream(ifName, prefix));
+ verify(mIOffloadMock).removeDownstream(eq(ifName), eq(prefix));
+ }
+
+ @Test
+ public void testRemoveDownstreamSuccess() throws Exception {
+ doTestRemoveDownstream(true);
+ }
+
+ @Test
+ public void testRemoveDownstreamFailure() throws Exception {
+ doTestRemoveDownstream(false);
+ }
+
+ @Test
+ public void testTetheringOffloadCallback() throws Exception {
+ initAndValidateOffloadHal(true);
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ final InOrder inOrder = inOrder(mOffloadHalCallback);
+ inOrder.verify(mOffloadHalCallback).onStarted();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onStoppedError();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onStoppedUnsupported();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onSupportAvailable();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onStoppedLimitReached();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onWarningReached();
+ inOrder.verifyNoMoreInteractions();
+
+ final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
+ mTetheringOffloadCallback.updateTimeout(tcpParams);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
+ eq(tcpParams.src.addr),
+ eq(tcpParams.src.port),
+ eq(tcpParams.dst.addr),
+ eq(tcpParams.dst.port));
+ inOrder.verifyNoMoreInteractions();
+
+ final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
+ mTetheringOffloadCallback.updateTimeout(udpParams);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
+ eq(udpParams.src.addr),
+ eq(udpParams.src.port),
+ eq(udpParams.dst.addr),
+ eq(udpParams.dst.port));
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
+ final NatTimeoutUpdate params = new NatTimeoutUpdate();
+ params.proto = proto;
+ params.src = new IPv4AddrPortPair();
+ params.dst = new IPv4AddrPortPair();
+ params.src.addr = "192.168.43.200";
+ params.src.port = 100;
+ params.dst.addr = "172.50.46.169";
+ params.dst.port = 150;
+ return params;
+ }
+}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java
new file mode 100644
index 0000000..6fdab5a
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.networkstack.tethering;
+
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.test.TestLooper;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.SharedLog;
+import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class OffloadHalHidlImplTest {
+ private static final String RMNET0 = "test_rmnet_data0";
+
+ private final SharedLog mLog = new SharedLog("test");
+ private final TestLooper mTestLooper = new TestLooper();
+
+ private OffloadHalHidlImpl mIOffloadHal;
+ private IOffloadConfig mIOffloadConfigMock;
+ private IOffloadControl mIOffloadControlMock;
+ private ITetheringOffloadCallback mTetheringOffloadCallback;
+ private OffloadHalCallback mOffloadHalCallback;
+
+ private void createAndInitOffloadHal(int version) throws Exception {
+ final FileDescriptor fd1 = new FileDescriptor();
+ final FileDescriptor fd2 = new FileDescriptor();
+ final NativeHandle handle1 = new NativeHandle(fd1, true);
+ final NativeHandle handle2 = new NativeHandle(fd2, true);
+ mIOffloadConfigMock = mock(IOffloadConfig.class);
+ switch (version) {
+ case OFFLOAD_HAL_VERSION_HIDL_1_0:
+ mIOffloadControlMock = mock(IOffloadControl.class);
+ break;
+ case OFFLOAD_HAL_VERSION_HIDL_1_1:
+ mIOffloadControlMock = mock(
+ android.hardware.tetheroffload.control.V1_1.IOffloadControl.class);
+ break;
+ default:
+ fail("Nonexistent HAL version");
+ return;
+ }
+ mIOffloadHal = new OffloadHalHidlImpl(version, mIOffloadConfigMock,
+ mIOffloadControlMock, new Handler(mTestLooper.getLooper()), mLog);
+ mIOffloadHal.initOffload(handle1, handle2, mOffloadHalCallback);
+
+ final ArgumentCaptor<NativeHandle> nativeHandleCaptor1 =
+ ArgumentCaptor.forClass(NativeHandle.class);
+ final ArgumentCaptor<NativeHandle> nativeHandleCaptor2 =
+ ArgumentCaptor.forClass(NativeHandle.class);
+ final ArgumentCaptor<ITetheringOffloadCallback> offloadCallbackCaptor =
+ ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
+ verify(mIOffloadConfigMock).setHandles(nativeHandleCaptor1.capture(),
+ nativeHandleCaptor2.capture(), any());
+ verify(mIOffloadControlMock).initOffload(offloadCallbackCaptor.capture(), any());
+ assertEquals(nativeHandleCaptor1.getValue().getFileDescriptor().getInt$(),
+ handle1.getFileDescriptor().getInt$());
+ assertEquals(nativeHandleCaptor2.getValue().getFileDescriptor().getInt$(),
+ handle2.getFileDescriptor().getInt$());
+ mTetheringOffloadCallback = offloadCallbackCaptor.getValue();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mOffloadHalCallback = spy(new OffloadHalCallback());
+ }
+
+ @Test
+ public void testGetForwardedStats() throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final long rxBytes = 12345;
+ final long txBytes = 67890;
+ doAnswer(invocation -> {
+ ((IOffloadControl.getForwardedStatsCallback) invocation.getArgument(1))
+ .onValues(rxBytes, txBytes);
+ return null;
+ }).when(mIOffloadControlMock).getForwardedStats(eq(RMNET0), any());
+ final ForwardedStats stats = mIOffloadHal.getForwardedStats(RMNET0);
+ verify(mIOffloadControlMock).getForwardedStats(eq(RMNET0), any());
+ assertNotNull(stats);
+ assertEquals(rxBytes, stats.rxBytes);
+ assertEquals(txBytes, stats.txBytes);
+ }
+
+ private void doTestSetLocalPrefixes(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final ArrayList<String> localPrefixes = new ArrayList<>();
+ localPrefixes.add("127.0.0.0/8");
+ localPrefixes.add("fe80::/64");
+ doAnswer(invocation -> {
+ ((IOffloadControl.setLocalPrefixesCallback) invocation.getArgument(1))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when(mIOffloadControlMock).setLocalPrefixes(eq(localPrefixes), any());
+ assertEquals(expectSuccess, mIOffloadHal.setLocalPrefixes(localPrefixes));
+ verify(mIOffloadControlMock).setLocalPrefixes(eq(localPrefixes), any());
+ }
+
+ @Test
+ public void testSetLocalPrefixesSuccess() throws Exception {
+ doTestSetLocalPrefixes(true);
+ }
+
+ @Test
+ public void testSetLocalPrefixesFailure() throws Exception {
+ doTestSetLocalPrefixes(false);
+ }
+
+ private void doTestSetDataLimit(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final long limit = 12345;
+ doAnswer(invocation -> {
+ ((IOffloadControl.setDataLimitCallback) invocation.getArgument(2))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when(mIOffloadControlMock).setDataLimit(eq(RMNET0), eq(limit), any());
+ assertEquals(expectSuccess, mIOffloadHal.setDataLimit(RMNET0, limit));
+ verify(mIOffloadControlMock).setDataLimit(eq(RMNET0), eq(limit), any());
+ }
+
+ @Test
+ public void testSetDataLimitSuccess() throws Exception {
+ doTestSetDataLimit(true);
+ }
+
+ @Test
+ public void testSetDataLimitFailure() throws Exception {
+ doTestSetDataLimit(false);
+ }
+
+ private void doTestSetDataWarningAndLimit(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_1);
+ final long warning = 12345;
+ final long limit = 67890;
+ doAnswer(invocation -> {
+ ((android.hardware.tetheroffload.control.V1_1.IOffloadControl
+ .setDataWarningAndLimitCallback) invocation.getArgument(3))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControlMock)
+ .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any());
+ assertEquals(expectSuccess, mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit));
+ verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControlMock)
+ .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any());
+ }
+
+ @Test
+ public void testSetDataWarningAndLimitSuccess() throws Exception {
+ doTestSetDataWarningAndLimit(true);
+ }
+
+ @Test
+ public void testSetDataWarningAndLimitFailure() throws Exception {
+ // Verify that V1.0 control HAL would reject the function call with exception.
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final long warning = 12345;
+ final long limit = 67890;
+ assertThrows(UnsupportedOperationException.class,
+ () -> mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit));
+
+ doTestSetDataWarningAndLimit(false);
+ }
+
+ private void doTestSetUpstreamParameters(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final String v4addr = "192.168.10.1";
+ final String v4gateway = "192.168.10.255";
+ final ArrayList<String> v6gws = new ArrayList<>(0);
+ v6gws.add("2001:db8::1");
+ doAnswer(invocation -> {
+ ((IOffloadControl.setUpstreamParametersCallback) invocation.getArgument(4))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when(mIOffloadControlMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+ eq(v6gws), any());
+ assertEquals(expectSuccess, mIOffloadHal.setUpstreamParameters(RMNET0, v4addr, v4gateway,
+ v6gws));
+ verify(mIOffloadControlMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+ eq(v6gws), any());
+ }
+
+ @Test
+ public void testSetUpstreamParametersSuccess() throws Exception {
+ doTestSetUpstreamParameters(true);
+ }
+
+ @Test
+ public void testSetUpstreamParametersFailure() throws Exception {
+ doTestSetUpstreamParameters(false);
+ }
+
+ private void doTestAddDownstream(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ doAnswer(invocation -> {
+ ((IOffloadControl.addDownstreamCallback) invocation.getArgument(2))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when(mIOffloadControlMock).addDownstream(eq(ifName), eq(prefix), any());
+ assertEquals(expectSuccess, mIOffloadHal.addDownstream(ifName, prefix));
+ verify(mIOffloadControlMock).addDownstream(eq(ifName), eq(prefix), any());
+ }
+
+ @Test
+ public void testAddDownstreamSuccess() throws Exception {
+ doTestAddDownstream(true);
+ }
+
+ @Test
+ public void testAddDownstreamFailure() throws Exception {
+ doTestAddDownstream(false);
+ }
+
+ private void doTestRemoveDownstream(boolean expectSuccess) throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final String ifName = "wlan1";
+ final String prefix = "192.168.43.0/24";
+ doAnswer(invocation -> {
+ ((IOffloadControl.removeDownstreamCallback) invocation.getArgument(2))
+ .onValues(expectSuccess, "");
+ return null;
+ }).when(mIOffloadControlMock).removeDownstream(eq(ifName), eq(prefix), any());
+ assertEquals(expectSuccess, mIOffloadHal.removeDownstream(ifName, prefix));
+ verify(mIOffloadControlMock).removeDownstream(eq(ifName), eq(prefix), any());
+ }
+
+ @Test
+ public void testRemoveDownstreamSuccess() throws Exception {
+ doTestRemoveDownstream(true);
+ }
+
+ @Test
+ public void testRemoveDownstreamFailure() throws Exception {
+ doTestRemoveDownstream(false);
+ }
+
+ @Test
+ public void testTetheringOffloadCallback() throws Exception {
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0);
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onStarted();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onStoppedError();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onStoppedUnsupported();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onSupportAvailable();
+
+ mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onStoppedLimitReached();
+
+ final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
+ mTetheringOffloadCallback.updateTimeout(tcpParams);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
+ eq(tcpParams.src.addr),
+ eq(uint16(tcpParams.src.port)),
+ eq(tcpParams.dst.addr),
+ eq(uint16(tcpParams.dst.port)));
+
+ final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
+ mTetheringOffloadCallback.updateTimeout(udpParams);
+ mTestLooper.dispatchAll();
+ verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
+ eq(udpParams.src.addr),
+ eq(uint16(udpParams.src.port)),
+ eq(udpParams.dst.addr),
+ eq(uint16(udpParams.dst.port)));
+ reset(mOffloadHalCallback);
+
+ createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_1);
+
+ // Verify the interface will process the events that comes from V1.1 HAL.
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ final InOrder inOrder = inOrder(mOffloadHalCallback);
+ inOrder.verify(mOffloadHalCallback).onStarted();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mOffloadHalCallback).onWarningReached();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
+ final NatTimeoutUpdate params = new NatTimeoutUpdate();
+ params.proto = proto;
+ params.src.addr = "192.168.43.200";
+ params.src.port = 100;
+ params.dst.addr = "172.50.46.169";
+ params.dst.port = 150;
+ return params;
+ }
+}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index 36b439b..b1f875b 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -20,36 +20,29 @@
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.SOCK_STREAM;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
-import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
-import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
-import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
-import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent;
import android.os.Handler;
import android.os.NativeHandle;
import android.os.test.TestLooper;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.OsConstants;
-import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -57,12 +50,13 @@
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.netlink.StructNfGenMsg;
import com.android.net.module.util.netlink.StructNlMsgHdr;
+import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -79,11 +73,9 @@
private final TestLooper mTestLooper = new TestLooper();
private OffloadHardwareInterface mOffloadHw;
- private ITetheringOffloadCallback mTetheringOffloadCallback;
- private OffloadHardwareInterface.ControlCallback mControlCallback;
+ private OffloadHalCallback mOffloadHalCallback;
- @Mock private IOffloadConfig mIOffloadConfig;
- private IOffloadControl mIOffloadControl;
+ @Mock private IOffloadHal mIOffload;
@Mock private NativeHandle mNativeHandle;
// Random values to test Netlink message.
@@ -91,32 +83,16 @@
private static final short TEST_FLAGS = 263;
class MyDependencies extends OffloadHardwareInterface.Dependencies {
- private final int mMockControlVersion;
- MyDependencies(SharedLog log, final int mockControlVersion) {
- super(log);
- mMockControlVersion = mockControlVersion;
+ private final int mMockOffloadHalVersion;
+ MyDependencies(Handler handler, SharedLog log, final int mockOffloadHalVersion) {
+ super(handler, log);
+ mMockOffloadHalVersion = mockOffloadHalVersion;
+ when(mIOffload.getVersion()).thenReturn(mMockOffloadHalVersion);
}
@Override
- public IOffloadConfig getOffloadConfig() {
- return mIOffloadConfig;
- }
-
- @Override
- public Pair<IOffloadControl, Integer> getOffloadControl() {
- switch (mMockControlVersion) {
- case OFFLOAD_HAL_VERSION_1_0:
- mIOffloadControl = mock(IOffloadControl.class);
- break;
- case OFFLOAD_HAL_VERSION_1_1:
- mIOffloadControl =
- mock(android.hardware.tetheroffload.control.V1_1.IOffloadControl.class);
- break;
- default:
- throw new IllegalArgumentException("Invalid offload control version "
- + mMockControlVersion);
- }
- return new Pair<IOffloadControl, Integer>(mIOffloadControl, mMockControlVersion);
+ public IOffloadHal getOffload() {
+ return mMockOffloadHalVersion == OFFLOAD_HAL_VERSION_NONE ? null : mIOffload;
}
@Override
@@ -128,156 +104,140 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
+ mOffloadHalCallback = new OffloadHalCallback();
+ when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class),
+ any(OffloadHalCallback.class))).thenReturn(true);
}
- private void startOffloadHardwareInterface(int controlVersion) throws Exception {
+ private void startOffloadHardwareInterface(int offloadHalVersion)
+ throws Exception {
final SharedLog log = new SharedLog("test");
- mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
- new MyDependencies(log, controlVersion));
- mOffloadHw.initOffloadConfig();
- mOffloadHw.initOffloadControl(mControlCallback);
- final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
- ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
- verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
- mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
+ final Handler handler = new Handler(mTestLooper.getLooper());
+ final int num = offloadHalVersion != OFFLOAD_HAL_VERSION_NONE ? 1 : 0;
+ mOffloadHw = new OffloadHardwareInterface(handler, log,
+ new MyDependencies(handler, log, offloadHalVersion));
+ assertEquals(offloadHalVersion, mOffloadHw.initOffload(mOffloadHalCallback));
+ verify(mIOffload, times(num)).initOffload(any(NativeHandle.class), any(NativeHandle.class),
+ eq(mOffloadHalCallback));
+ }
+
+ @Test
+ public void testInitFailureWithNoHal() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE);
+ }
+
+ @Test
+ public void testInitSuccessWithAidl() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL);
+ }
+
+ @Test
+ public void testInitSuccessWithHidl_1_0() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ }
+
+ @Test
+ public void testInitSuccessWithHidl_1_1() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
}
@Test
public void testGetForwardedStats() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
- final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
- verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
- assertNotNull(stats);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ ForwardedStats stats = new ForwardedStats(12345, 56780);
+ when(mIOffload.getForwardedStats(anyString())).thenReturn(stats);
+ assertEquals(mOffloadHw.getForwardedStats(RMNET0), stats);
+ verify(mIOffload).getForwardedStats(eq(RMNET0));
}
@Test
public void testSetLocalPrefixes() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
final ArrayList<String> localPrefixes = new ArrayList<>();
localPrefixes.add("127.0.0.0/8");
localPrefixes.add("fe80::/64");
- mOffloadHw.setLocalPrefixes(localPrefixes);
- verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
+ when(mIOffload.setLocalPrefixes(any())).thenReturn(true);
+ assertTrue(mOffloadHw.setLocalPrefixes(localPrefixes));
+ verify(mIOffload).setLocalPrefixes(eq(localPrefixes));
+ when(mIOffload.setLocalPrefixes(any())).thenReturn(false);
+ assertFalse(mOffloadHw.setLocalPrefixes(localPrefixes));
}
@Test
public void testSetDataLimit() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
final long limit = 12345;
- mOffloadHw.setDataLimit(RMNET0, limit);
- verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
+ when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(true);
+ assertTrue(mOffloadHw.setDataLimit(RMNET0, limit));
+ verify(mIOffload).setDataLimit(eq(RMNET0), eq(limit));
+ when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(false);
+ assertFalse(mOffloadHw.setDataLimit(RMNET0, limit));
+ }
+
+ @Test
+ public void testSetDataWarningAndLimitFailureWithHidl_1_0() throws Exception {
+ // Verify V1.0 control HAL would reject the function call with exception.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
+ final long warning = 12345;
+ final long limit = 67890;
+ assertThrows(UnsupportedOperationException.class,
+ () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
}
@Test
public void testSetDataWarningAndLimit() throws Exception {
- // Verify V1.0 control HAL would reject the function call with exception.
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ // Verify V1.1 control HAL could receive this function call.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1);
final long warning = 12345;
final long limit = 67890;
- assertThrows(IllegalArgumentException.class,
- () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
- reset(mIOffloadControl);
-
- // Verify V1.1 control HAL could receive this function call.
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
- mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit);
- verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl)
- .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any());
+ when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
+ assertTrue(mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
+ verify(mIOffload).setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit));
+ when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(false);
+ assertFalse(mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
}
@Test
public void testSetUpstreamParameters() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
final String v4addr = "192.168.10.1";
final String v4gateway = "192.168.10.255";
final ArrayList<String> v6gws = new ArrayList<>(0);
v6gws.add("2001:db8::1");
- mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
- verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
- eq(v6gws), any());
+ when(mIOffload.setUpstreamParameters(anyString(), anyString(), anyString(), any()))
+ .thenReturn(true);
+ assertTrue(mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws));
+ verify(mIOffload).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), eq(v6gws));
final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- mOffloadHw.setUpstreamParameters(null, null, null, null);
- verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
- mArrayListCaptor.capture(), any());
+ when(mIOffload.setUpstreamParameters(anyString(), anyString(), anyString(), any()))
+ .thenReturn(false);
+ assertFalse(mOffloadHw.setUpstreamParameters(null, null, null, null));
+ verify(mIOffload).setUpstreamParameters(eq(""), eq(""), eq(""), mArrayListCaptor.capture());
assertEquals(mArrayListCaptor.getValue().size(), 0);
}
@Test
- public void testUpdateDownstreamPrefix() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ public void testUpdateDownstream() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
final String ifName = "wlan1";
final String prefix = "192.168.43.0/24";
- mOffloadHw.addDownstreamPrefix(ifName, prefix);
- verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
-
- mOffloadHw.removeDownstreamPrefix(ifName, prefix);
- verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
- }
-
- @Test
- public void testTetheringOffloadCallback() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStarted();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedError();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedUnsupported();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onSupportAvailable();
-
- mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onStoppedLimitReached();
-
- final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
- mTetheringOffloadCallback.updateTimeout(tcpParams);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
- eq(tcpParams.src.addr),
- eq(uint16(tcpParams.src.port)),
- eq(tcpParams.dst.addr),
- eq(uint16(tcpParams.dst.port)));
-
- final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
- mTetheringOffloadCallback.updateTimeout(udpParams);
- mTestLooper.dispatchAll();
- verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
- eq(udpParams.src.addr),
- eq(uint16(udpParams.src.port)),
- eq(udpParams.dst.addr),
- eq(uint16(udpParams.dst.port)));
- reset(mControlCallback);
-
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
-
- // Verify the interface will process the events that comes from V1.1 HAL.
- mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED);
- mTestLooper.dispatchAll();
- final InOrder inOrder = inOrder(mControlCallback);
- inOrder.verify(mControlCallback).onStarted();
- inOrder.verifyNoMoreInteractions();
-
- mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED);
- mTestLooper.dispatchAll();
- inOrder.verify(mControlCallback).onWarningReached();
- inOrder.verifyNoMoreInteractions();
+ when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(true);
+ assertTrue(mOffloadHw.addDownstream(ifName, prefix));
+ verify(mIOffload).addDownstream(eq(ifName), eq(prefix));
+ when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(false);
+ assertFalse(mOffloadHw.addDownstream(ifName, prefix));
+ when(mIOffload.removeDownstream(anyString(), anyString())).thenReturn(true);
+ assertTrue(mOffloadHw.removeDownstream(ifName, prefix));
+ verify(mIOffload).removeDownstream(eq(ifName), eq(prefix));
+ when(mIOffload.removeDownstream(anyString(), anyString())).thenReturn(false);
+ assertFalse(mOffloadHw.removeDownstream(ifName, prefix));
}
@Test
public void testSendIpv4NfGenMsg() throws Exception {
- startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0);
FileDescriptor writeSocket = new FileDescriptor();
FileDescriptor readSocket = new FileDescriptor();
try {
@@ -308,14 +268,4 @@
assertEquals(0 /* error */, buffer.getShort()); // res_id
assertEquals(expectedLen, buffer.position());
}
-
- private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
- final NatTimeoutUpdate params = new NatTimeoutUpdate();
- params.proto = proto;
- params.src.addr = "192.168.43.200";
- params.src.port = 100;
- params.dst.addr = "172.50.46.169";
- params.dst.port = 150;
- return params;
- }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 75c819b..ac3d713 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -156,6 +156,7 @@
@After
fun tearDown() {
fakeTetheringThread.quitSafely()
+ fakeTetheringThread.join()
}
private fun verifyActivityPendingIntent(intent: Intent, flags: Int) {
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 79590b7..adb1590 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -68,7 +68,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
-import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
@@ -649,8 +649,7 @@
mInterfaceConfiguration.flags = new String[0];
when(mRouterAdvertisementDaemon.start())
.thenReturn(true);
- initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
- 0 /* defaultDisabled */);
+ initOffloadConfiguration(OFFLOAD_HAL_VERSION_HIDL_1_0, 0 /* defaultDisabled */);
when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
mServiceContext = new TestContext(mContext);
@@ -2345,25 +2344,15 @@
mLooper.dispatchAll();
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- // 1. Offload fail if no OffloadConfig.
- initOffloadConfiguration(false /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
- 0 /* defaultDisabled */);
+ // 1. Offload fail if no IOffloadHal.
+ initOffloadConfiguration(OFFLOAD_HAL_VERSION_NONE, 0 /* defaultDisabled */);
runUsbTethering(upstreamState);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
runStopUSBTethering();
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
reset(mUsbManager, mIPv6TetheringCoordinator);
- // 2. Offload fail if no OffloadControl.
- initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_NONE,
- 0 /* defaultDisabled */);
- runUsbTethering(upstreamState);
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
- runStopUSBTethering();
- callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
- reset(mUsbManager, mIPv6TetheringCoordinator);
- // 3. Offload fail if disabled by settings.
- initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
- 1 /* defaultDisabled */);
+ // 2. Offload fail if disabled by settings.
+ initOffloadConfiguration(OFFLOAD_HAL_VERSION_HIDL_1_0, 1 /* defaultDisabled */);
runUsbTethering(upstreamState);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
runStopUSBTethering();
@@ -2378,11 +2367,10 @@
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
}
- private void initOffloadConfiguration(final boolean offloadConfig,
- @OffloadHardwareInterface.OffloadHalVersion final int offloadControlVersion,
+ private void initOffloadConfiguration(
+ @OffloadHardwareInterface.OffloadHalVersion final int offloadHalVersion,
final int defaultDisabled) {
- when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig);
- when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControlVersion);
+ when(mOffloadHardwareInterface.initOffload(any())).thenReturn(offloadHalVersion);
when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn(
defaultDisabled);
}
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 7350209..7c6811a 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -91,6 +91,12 @@
if (ip6->version != 6) return TC_ACT_PIPE;
// Maximum IPv6 payload length that can be translated to IPv4
+ // Note: technically this check is too strict for an IPv6 fragment,
+ // which by virtue of stripping the extra 8 byte fragment extension header,
+ // could thus be 8 bytes larger and still fit in an ipv4 packet post
+ // translation. However... who ever heard of receiving ~64KB frags...
+ // fragments are kind of by definition smaller than ingress device mtu,
+ // and thus, on the internet, very very unlikely to exceed 1500 bytes.
if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_PIPE;
ClatIngress6Key k = {
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index 391a562..b8eb1f6 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -16,15 +16,13 @@
filegroup {
name: "framework-connectivity-tiramisu-updatable-sources",
+ defaults: ["framework-sources-module-defaults"],
srcs: [
"src/**/*.java",
"src/**/*.aidl",
],
path: "src",
- visibility: [
- "//frameworks/base",
- "//packages/modules/Connectivity:__subpackages__",
- ],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
cc_library_shared {
diff --git a/framework-t/src/android/net/nsd/INsdManager.aidl b/framework-t/src/android/net/nsd/INsdManager.aidl
index 89e9cdb..9d14b1a 100644
--- a/framework-t/src/android/net/nsd/INsdManager.aidl
+++ b/framework-t/src/android/net/nsd/INsdManager.aidl
@@ -26,5 +26,5 @@
* {@hide}
*/
interface INsdManager {
- INsdServiceConnector connect(INsdManagerCallback cb);
+ INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend);
}
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 36808cf..96f2f80 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,6 +16,7 @@
package android.net.nsd;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
import android.annotation.IntDef;
@@ -510,7 +511,8 @@
mHandler = new ServiceHandler(t.getLooper());
try {
- mService = service.connect(new NsdCallbackImpl(mHandler));
+ mService = service.connect(new NsdCallbackImpl(mHandler), CompatChanges.isChangeEnabled(
+ ENABLE_PLATFORM_MDNS_BACKEND));
} catch (RemoteException e) {
throw new RuntimeException("Failed to connect to NsdService");
}
diff --git a/framework/Android.bp b/framework/Android.bp
index 3950dba..2d729c5 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -45,14 +45,12 @@
// TODO: use a java_library in the bootclasspath instead
filegroup {
name: "framework-connectivity-sources",
+ defaults: ["framework-sources-module-defaults"],
srcs: [
":framework-connectivity-internal-sources",
":framework-connectivity-aidl-export-sources",
],
- visibility: [
- "//frameworks/base",
- "//packages/modules/Connectivity:__subpackages__",
- ],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
}
java_defaults {
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 2cfda9e..dfe5867 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -73,6 +73,17 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION = 266524688;
+ /**
+ * Apps targeting < Android 14 use a legacy NSD backend.
+ *
+ * The legacy apps use a legacy native daemon as NsdManager backend, but other apps use a
+ * platform-integrated mDNS implementation as backend.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long ENABLE_PLATFORM_MDNS_BACKEND = 270306772L;
private ConnectivityCompatChanges() {
}
}
diff --git a/nearby/framework/Android.bp b/nearby/framework/Android.bp
index e223b54..f6e0995 100644
--- a/nearby/framework/Android.bp
+++ b/nearby/framework/Android.bp
@@ -32,10 +32,10 @@
filegroup {
name: "framework-nearby-sources",
+ defaults: ["framework-sources-module-defaults"],
srcs: [
":framework-nearby-java-sources",
],
- visibility: ["//frameworks/base"],
}
// Build of only framework-nearby (not as part of connectivity) for
diff --git a/nearby/tests/multidevices/clients/test_support/snippet_helper/tests/Android.bp b/nearby/tests/multidevices/clients/test_support/snippet_helper/tests/Android.bp
index 284d5c2..ec0392c 100644
--- a/nearby/tests/multidevices/clients/test_support/snippet_helper/tests/Android.bp
+++ b/nearby/tests/multidevices/clients/test_support/snippet_helper/tests/Android.bp
@@ -35,4 +35,5 @@
// timeout in seconds.
timeout: 36000,
},
-}
\ No newline at end of file
+ upstream: true,
+}
diff --git a/nearby/tests/robotests/Android.bp b/nearby/tests/robotests/Android.bp
index 56c0107..70fa0c3 100644
--- a/nearby/tests/robotests/Android.bp
+++ b/nearby/tests/robotests/Android.bp
@@ -42,15 +42,14 @@
"androidx.lifecycle_lifecycle-runtime",
"androidx.mediarouter_mediarouter-nodeps",
"error_prone_annotations",
- "mockito-robolectric-prebuilt",
"service-nearby-pre-jarjar",
"truth-prebuilt",
"robolectric_android-all-stub",
- "Robolectric_all-target",
],
test_options: {
// timeout in seconds.
timeout: 36000,
},
+ upstream: true,
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index c92e9a9..cbe6691 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -53,7 +53,6 @@
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;
@@ -397,13 +396,12 @@
final int clientId = msg.arg2;
switch (msg.what) {
case NsdManager.REGISTER_CLIENT:
- final Pair<NsdServiceConnector, INsdManagerCallback> arg =
- (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
- final INsdManagerCallback cb = arg.second;
+ final ConnectorArgs arg = (ConnectorArgs) msg.obj;
+ final INsdManagerCallback cb = arg.callback;
try {
- cb.asBinder().linkToDeath(arg.first, 0);
- cInfo = new ClientInfo(cb);
- mClients.put(arg.first, cInfo);
+ cb.asBinder().linkToDeath(arg.connector, 0);
+ cInfo = new ClientInfo(cb, arg.useJavaBackend);
+ mClients.put(arg.connector, cInfo);
} catch (RemoteException e) {
Log.w(TAG, "Client " + clientId + " has already died");
}
@@ -608,7 +606,8 @@
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
final String serviceType = constructServiceType(info.getServiceType());
- if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)
+ if (clientInfo.mUseJavaBackend
+ || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
clientInfo.onDiscoverServicesFailed(clientId,
@@ -702,7 +701,8 @@
final NsdServiceInfo serviceInfo = args.serviceInfo;
final String serviceType = serviceInfo.getServiceType();
final String registerServiceType = constructServiceType(serviceType);
- if (mDeps.isMdnsAdvertiserEnabled(mContext)
+ if (clientInfo.mUseJavaBackend
+ || mDeps.isMdnsAdvertiserEnabled(mContext)
|| useAdvertiserForType(registerServiceType)) {
if (registerServiceType == null) {
Log.e(TAG, "Invalid service type: " + serviceType);
@@ -782,7 +782,8 @@
final NsdServiceInfo info = args.serviceInfo;
id = getUniqueId();
final String serviceType = constructServiceType(info.getServiceType());
- if (mDeps.isMdnsDiscoveryManagerEnabled(mContext)
+ if (clientInfo.mUseJavaBackend
+ || mDeps.isMdnsDiscoveryManagerEnabled(mContext)
|| useDiscoveryManagerForType(serviceType)) {
if (serviceType == null) {
clientInfo.onResolveServiceFailed(clientId,
@@ -1532,12 +1533,27 @@
}
}
+ private static class ConnectorArgs {
+ @NonNull public final NsdServiceConnector connector;
+ @NonNull public final INsdManagerCallback callback;
+ public final boolean useJavaBackend;
+
+ ConnectorArgs(@NonNull NsdServiceConnector connector, @NonNull INsdManagerCallback callback,
+ boolean useJavaBackend) {
+ this.connector = connector;
+ this.callback = callback;
+ this.useJavaBackend = useJavaBackend;
+ }
+ }
+
@Override
- public INsdServiceConnector connect(INsdManagerCallback cb) {
+ public INsdServiceConnector connect(INsdManagerCallback cb, boolean useJavaBackend) {
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 Pair<>(connector, cb)));
+ NsdManager.REGISTER_CLIENT,
+ new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend)));
return connector;
}
@@ -1793,9 +1809,12 @@
// The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsPreSClient = false;
+ // The flag of using java backend if the client's target SDK >= U
+ private final boolean mUseJavaBackend;
- private ClientInfo(INsdManagerCallback cb) {
+ private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend) {
mCb = cb;
+ mUseJavaBackend = useJavaBackend;
if (DBG) Log.d(TAG, "New client");
}
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 be2555b..e0100f6 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -81,6 +81,21 @@
return a == null || a.getTtl() == b.getTtl();
}
+ private <T extends MdnsRecord> boolean addOrReplaceRecord(@NonNull T record,
+ @NonNull List<T> recordsList) {
+ final int existing = recordsList.indexOf(record);
+ if (existing >= 0) {
+ if (recordsAreSame(record, recordsList.get(existing))) {
+ return false;
+ }
+ final MdnsRecord existedRecord = recordsList.remove(existing);
+ records.remove(existedRecord);
+ }
+ recordsList.add(record);
+ records.add(record);
+ return true;
+ }
+
/**
* Adds a pointer record.
*
@@ -92,17 +107,7 @@
throw new IllegalArgumentException(
"Pointer records for different service names cannot be added");
}
- final int existing = pointerRecords.indexOf(pointerRecord);
- if (existing >= 0) {
- if (recordsAreSame(pointerRecord, pointerRecords.get(existing))) {
- return false;
- }
- final MdnsRecord record = pointerRecords.remove(existing);
- records.remove(record);
- }
- pointerRecords.add(pointerRecord);
- records.add(pointerRecord);
- return true;
+ return addOrReplaceRecord(pointerRecord, pointerRecords);
}
/** Gets the pointer records. */
@@ -207,17 +212,7 @@
/** Add the IPv4 address record. */
public synchronized boolean addInet4AddressRecord(
@NonNull MdnsInetAddressRecord newInet4AddressRecord) {
- final int existing = inet4AddressRecords.indexOf(newInet4AddressRecord);
- if (existing >= 0) {
- if (recordsAreSame(newInet4AddressRecord, inet4AddressRecords.get(existing))) {
- return false;
- }
- final MdnsRecord record = inet4AddressRecords.remove(existing);
- records.remove(record);
- }
- inet4AddressRecords.add(newInet4AddressRecord);
- records.add(newInet4AddressRecord);
- return true;
+ return addOrReplaceRecord(newInet4AddressRecord, inet4AddressRecords);
}
/** Gets the IPv4 address records. */
@@ -248,17 +243,7 @@
/** Sets the IPv6 address records. */
public synchronized boolean addInet6AddressRecord(
@NonNull MdnsInetAddressRecord newInet6AddressRecord) {
- final int existing = inet6AddressRecords.indexOf(newInet6AddressRecord);
- if (existing >= 0) {
- if (recordsAreSame(newInet6AddressRecord, inet6AddressRecords.get(existing))) {
- return false;
- }
- final MdnsRecord record = inet6AddressRecords.remove(existing);
- records.remove(record);
- }
- inet6AddressRecords.add(newInet6AddressRecord);
- records.add(newInet6AddressRecord);
- return true;
+ return addOrReplaceRecord(newInet6AddressRecord, inet6AddressRecords);
}
/**
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 0151202..129db7e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -220,9 +220,10 @@
// This bit, the cache-flush bit, tells neighboring hosts
// that this is not a shared record type. Instead of merging this new
// record additively into the cache in addition to any previous records with
- // the same name, rrtype, and rrclass, all old records with that name,
- // rrtype, and rrclass that were received more than one second ago are
- // declared invalid, and marked to expire from the cache in one second.
+ // the same name, rrtype, and rrclass.
+ // TODO: All old records with that name, rrtype, and rrclass that were
+ // received more than one second ago are declared invalid, and marked
+ // to expire from the cache in one second.
if (inetRecord.getCacheFlush()) {
response.clearInet4AddressRecords();
response.clearInet6AddressRecords();
@@ -236,9 +237,10 @@
// This bit, the cache-flush bit, tells neighboring hosts
// that this is not a shared record type. Instead of merging this new
// record additively into the cache in addition to any previous records with
- // the same name, rrtype, and rrclass, all old records with that name,
- // rrtype, and rrclass that were received more than one second ago are
- // declared invalid, and marked to expire from the cache in one second.
+ // the same name, rrtype, and rrclass.
+ // TODO: All old records with that name, rrtype, and rrclass that were
+ // received more than one second ago are declared invalid, and marked
+ // to expire from the cache in one second.
if (inetRecord.getCacheFlush()) {
response.clearInet4AddressRecords();
response.clearInet6AddressRecords();
diff --git a/service/src/com/android/metrics/stats.proto b/service/src/com/android/metrics/stats.proto
index 8104632..006d20a 100644
--- a/service/src/com/android/metrics/stats.proto
+++ b/service/src/com/android/metrics/stats.proto
@@ -347,3 +347,89 @@
// How long(in seconds) this slice has been connected
optional int32 slice_connection_duration_sec = 6;
}
+
+/**
+ * Logs DailykeepaliveInfoReported
+ *
+ * Logs from: packages/modules/Connectivity/service/src/com/android/
+ * server/connectivity/AutomaticOnOffKeepaliveTracker.
+ */
+message DailykeepaliveInfoReported{
+ // Daily duration per number of concurrent keepalive
+ optional DurationPerNumOfKeepalive duration_per_num_of_keepalive = 1;
+
+ // Daily keepalive registered/active duration on each list of keepalive session, in
+ // milli-seconds
+ optional KeepaliveLifetimePerCarrier keepalive_lifetime_per_carrier = 2;
+
+ // Daily number of keepalive requests
+ optional int32 keepalive_requests = 3;
+
+ // Daily number of automatic keepalive requests
+ optional int32 automatic_keepalive_requests = 4;
+
+ // Daily number of distinct apps that requested keepalives
+ optional int32 distinct_user_count = 5;
+
+ // Daily distinct apps uid list that requested keepalives
+ repeated int32 uid = 6;
+}
+
+/**
+ * Daily duration per number of concurrent keepalive
+ *
+ * Logs from: packages/modules/Connectivity/service/src/com/android/
+ * server/connectivity/AutomaticOnOffKeepaliveTracker.
+ */
+message DurationPerNumOfKeepalive {
+ repeated DurationForNumOfKeepalive duration_for_num_of_keepalive = 1;
+}
+
+message DurationForNumOfKeepalive {
+ // The number of concurrent keepalives is in the device
+ optional int32 num_of_keepalive = 1;
+
+ // How many milliseconds the device has keepalive registration number is num_of_keepalive
+ optional int32 keepalive_registered_durations_msec = 2;
+
+ // How many milliseconds the device has keepalive active(not paused) number is num_of_keepalive
+ optional int32 keepalive_active_durations_msec = 3;
+}
+
+/**
+ * Daily keepalive registered/active duration on each list of Keepalive session, in milli-seconds
+ *
+ * Logs from: packages/modules/Connectivity/service/src/com/android/
+ * server/connectivity/AutomaticOnOffKeepaliveTracker.
+ */
+message KeepaliveLifetimePerCarrier {
+ // The number of network count on each list of carriers
+ repeated KeepaliveLifetimeForCarrier keepalive_lifetime_for_carrier = 1;
+}
+
+/**
+ * Logs the keepalive registered/active duration in milli-seconds and carrier
+ * info(carrier id, transport, keepalive interval).
+ *
+ * Logs from: packages/modules/Connectivity/service/src/com/android/
+ * server/connectivity/AutomaticOnOffKeepaliveTracker.
+ */
+message KeepaliveLifetimeForCarrier {
+ // The carrier ID for each keepalive, or TelephonyManager.UNKNOWN_CARRIER_ID(-1) if not cell
+ optional int32 carrier_id = 1;
+
+ // The transport types of the underlying network for each keepalive. A network may include
+ // multiple transport types. Each transfer type is represented by a different bit, defined in
+ // packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java
+ optional int32 transport_types = 2;
+
+ // The keepalive interval for each keepalive
+ optional int32 intervals_msec = 3;
+
+ // The lifetime of the keepalive registered today
+ optional int32 lifetime_msec = 4;
+
+ // The duration for which the keepalive was active (not suspended)
+ optional int32 active_lifetime_msec = 5;
+}
+
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index e969cd6..2af30dd 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -10734,6 +10734,18 @@
callback));
}
+ private boolean hasUnderlyingTestNetworks(NetworkCapabilities nc) {
+ final List<Network> underlyingNetworks = nc.getUnderlyingNetworks();
+ if (underlyingNetworks == null) return false;
+
+ for (Network network : underlyingNetworks) {
+ if (getNetworkCapabilitiesInternal(network).hasTransport(TRANSPORT_TEST)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void simulateDataStall(int detectionMethod, long timestampMillis,
@NonNull Network network, @NonNull PersistableBundle extras) {
@@ -10744,14 +10756,18 @@
android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_STACK);
final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
- if (!nc.hasTransport(TRANSPORT_TEST)) {
- throw new SecurityException("Data Stall simulation is only possible for test networks");
+ if (!nc.hasTransport(TRANSPORT_TEST) && !hasUnderlyingTestNetworks(nc)) {
+ throw new SecurityException(
+ "Data Stall simulation is only possible for test networks or networks built on"
+ + " top of test networks");
}
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai == null || nai.creatorUid != mDeps.getCallingUid()) {
- throw new SecurityException("Data Stall simulation is only possible for network "
- + "creators");
+ if (nai == null
+ || (nai.creatorUid != mDeps.getCallingUid()
+ && nai.creatorUid != Process.SYSTEM_UID)) {
+ throw new SecurityException(
+ "Data Stall simulation is only possible for network " + "creators");
}
// Instead of passing the data stall directly to the ConnectivityDiagnostics handler, treat
diff --git a/tests/common/java/android/net/NetworkProviderTest.kt b/tests/common/java/android/net/NetworkProviderTest.kt
index c0e7f61..fcbb0dd 100644
--- a/tests/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/common/java/android/net/NetworkProviderTest.kt
@@ -79,6 +79,7 @@
@After
fun tearDown() {
mHandlerThread.quitSafely()
+ mHandlerThread.join()
instrumentation.getUiAutomation().dropShellPermissionIdentity()
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
index 449454e..fe522a0 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -32,6 +32,7 @@
import com.android.networkstack.apishim.VpnServiceBuilderShimImpl;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.apishim.common.VpnServiceBuilderShim;
+import com.android.testutils.PacketReflector;
import java.io.IOException;
import java.net.InetAddress;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java
deleted file mode 100644
index 124c2c3..0000000
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2014 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.cts.net.hostside;
-
-import static android.system.OsConstants.ICMP6_ECHO_REPLY;
-import static android.system.OsConstants.ICMP6_ECHO_REQUEST;
-import static android.system.OsConstants.ICMP_ECHO;
-import static android.system.OsConstants.ICMP_ECHOREPLY;
-
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-public class PacketReflector extends Thread {
-
- private static int IPV4_HEADER_LENGTH = 20;
- private static int IPV6_HEADER_LENGTH = 40;
-
- private static int IPV4_ADDR_OFFSET = 12;
- private static int IPV6_ADDR_OFFSET = 8;
- private static int IPV4_ADDR_LENGTH = 4;
- private static int IPV6_ADDR_LENGTH = 16;
-
- private static int IPV4_PROTO_OFFSET = 9;
- private static int IPV6_PROTO_OFFSET = 6;
-
- private static final byte IPPROTO_ICMP = 1;
- private static final byte IPPROTO_TCP = 6;
- private static final byte IPPROTO_UDP = 17;
- private static final byte IPPROTO_ICMPV6 = 58;
-
- private static int ICMP_HEADER_LENGTH = 8;
- private static int TCP_HEADER_LENGTH = 20;
- private static int UDP_HEADER_LENGTH = 8;
-
- private static final byte ICMP_ECHO = 8;
- private static final byte ICMP_ECHOREPLY = 0;
-
- private static String TAG = "PacketReflector";
-
- private FileDescriptor mFd;
- private byte[] mBuf;
-
- public PacketReflector(FileDescriptor fd, int mtu) {
- super("PacketReflector");
- mFd = fd;
- mBuf = new byte[mtu];
- }
-
- private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
- for (int i = 0; i < len; i++) {
- byte b = buf[pos1 + i];
- buf[pos1 + i] = buf[pos2 + i];
- buf[pos2 + i] = b;
- }
- }
-
- private static void swapAddresses(byte[] buf, int version) {
- int addrPos, addrLen;
- switch(version) {
- case 4:
- addrPos = IPV4_ADDR_OFFSET;
- addrLen = IPV4_ADDR_LENGTH;
- break;
- case 6:
- addrPos = IPV6_ADDR_OFFSET;
- addrLen = IPV6_ADDR_LENGTH;
- break;
- default:
- throw new IllegalArgumentException();
- }
- swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
- }
-
- // Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
- // This is used by the test to "connect to itself" through the VPN.
- private void processTcpPacket(byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + TCP_HEADER_LENGTH) {
- return;
- }
-
- // Swap src and dst IP addresses.
- swapAddresses(buf, version);
-
- // Send the packet back.
- writePacket(buf, len);
- }
-
- // Echo UDP packets: swap source and destination addresses, and source and destination ports.
- // This is used by the test to check that the bytes it sends are echoed back.
- private void processUdpPacket(byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + UDP_HEADER_LENGTH) {
- return;
- }
-
- // Swap src and dst IP addresses.
- swapAddresses(buf, version);
-
- // Swap dst and src ports.
- int portOffset = hdrLen;
- swapBytes(buf, portOffset, portOffset + 2, 2);
-
- // Send the packet back.
- writePacket(buf, len);
- }
-
- private void processIcmpPacket(byte[] buf, int version, int len, int hdrLen) {
- if (len < hdrLen + ICMP_HEADER_LENGTH) {
- return;
- }
-
- byte type = buf[hdrLen];
- if (!(version == 4 && type == ICMP_ECHO) &&
- !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) {
- return;
- }
-
- // Save the ping packet we received.
- byte[] request = buf.clone();
-
- // Swap src and dst IP addresses, and send the packet back.
- // This effectively pings the device to see if it replies.
- swapAddresses(buf, version);
- writePacket(buf, len);
-
- // The device should have replied, and buf should now contain a ping response.
- int received = readPacket(buf);
- if (received != len) {
- Log.i(TAG, "Reflecting ping did not result in ping response: " +
- "read=" + received + " expected=" + len);
- return;
- }
-
- byte replyType = buf[hdrLen];
- if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY)
- || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) {
- Log.i(TAG, "Received unexpected ICMP reply: original " + type
- + ", reply " + replyType);
- return;
- }
-
- // Compare the response we got with the original packet.
- // The only thing that should have changed are addresses, type and checksum.
- // Overwrite them with the received bytes and see if the packet is otherwise identical.
- request[hdrLen] = buf[hdrLen]; // Type
- request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1.
- request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2.
-
- // Since Linux kernel 4.2, net.ipv6.auto_flowlabels is set by default, and therefore
- // the request and reply may have different IPv6 flow label: ignore that as well.
- if (version == 6) {
- request[1] = (byte)(request[1] & 0xf0 | buf[1] & 0x0f);
- request[2] = buf[2];
- request[3] = buf[3];
- }
-
- for (int i = 0; i < len; i++) {
- if (buf[i] != request[i]) {
- Log.i(TAG, "Received non-matching packet when expecting ping response.");
- return;
- }
- }
-
- // Now swap the addresses again and reflect the packet. This sends a ping reply.
- swapAddresses(buf, version);
- writePacket(buf, len);
- }
-
- private void writePacket(byte[] buf, int len) {
- try {
- Os.write(mFd, buf, 0, len);
- } catch (ErrnoException|IOException e) {
- Log.e(TAG, "Error writing packet: " + e.getMessage());
- }
- }
-
- private int readPacket(byte[] buf) {
- int len;
- try {
- len = Os.read(mFd, buf, 0, buf.length);
- } catch (ErrnoException|IOException e) {
- Log.e(TAG, "Error reading packet: " + e.getMessage());
- len = -1;
- }
- return len;
- }
-
- // Reads one packet from our mFd, and possibly writes the packet back.
- private void processPacket() {
- int len = readPacket(mBuf);
- if (len < 1) {
- return;
- }
-
- int version = mBuf[0] >> 4;
- int addrPos, protoPos, hdrLen, addrLen;
- if (version == 4) {
- hdrLen = IPV4_HEADER_LENGTH;
- protoPos = IPV4_PROTO_OFFSET;
- addrPos = IPV4_ADDR_OFFSET;
- addrLen = IPV4_ADDR_LENGTH;
- } else if (version == 6) {
- hdrLen = IPV6_HEADER_LENGTH;
- protoPos = IPV6_PROTO_OFFSET;
- addrPos = IPV6_ADDR_OFFSET;
- addrLen = IPV6_ADDR_LENGTH;
- } else {
- return;
- }
-
- if (len < hdrLen) {
- return;
- }
-
- byte proto = mBuf[protoPos];
- switch (proto) {
- case IPPROTO_ICMP:
- case IPPROTO_ICMPV6:
- processIcmpPacket(mBuf, version, len, hdrLen);
- break;
- case IPPROTO_TCP:
- processTcpPacket(mBuf, version, len, hdrLen);
- break;
- case IPPROTO_UDP:
- processUdpPacket(mBuf, version, len, hdrLen);
- break;
- }
- }
-
- public void run() {
- Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid());
- while (!interrupted() && mFd.valid()) {
- processPacket();
- }
- Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid());
- }
-}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index f578ff3..b535a8f 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -191,6 +191,7 @@
callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
qosTestSocket?.close()
mHandlerThread.quitSafely()
+ mHandlerThread.join()
instrumentation.getUiAutomation().dropShellPermissionIdentity()
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index eb41d71..fcfecad 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -90,6 +90,7 @@
mCm.unregisterNetworkCallback(agentCleanUpCb)
mHandlerThread.quitSafely()
+ mHandlerThread.join()
callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
index 8e98dba..621af23 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
@@ -146,6 +146,7 @@
httpServer.stop()
handlerThread.threadHandler.post { reader.stop() }
handlerThread.quitSafely()
+ handlerThread.join()
iface.fileDescriptor.close()
}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 6fd2321..db4f937 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -432,6 +432,7 @@
}
handlerThread.waitForIdle(TIMEOUT_MS)
handlerThread.quitSafely()
+ handlerThread.join()
}
@Test
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index da65b62..0965193 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -73,11 +74,11 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doReturn(mServiceConn).when(mService).connect(any());
+ doReturn(mServiceConn).when(mService).connect(any(), anyBoolean());
mManager = new NsdManager(mContext, mService);
final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
INsdManagerCallback.class);
- verify(mService).connect(cbCaptor.capture());
+ verify(mService).connect(cbCaptor.capture(), anyBoolean());
mCallback = cbCaptor.getValue();
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c1c6a8a..6c89c38 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2219,7 +2219,9 @@
ConnectivityResources.setResourcesContextForTest(null);
mCsHandlerThread.quitSafely();
+ mCsHandlerThread.join();
mAlarmManagerThread.quitSafely();
+ mAlarmManagerThread.join();
}
private void mockDefaultPackages() throws Exception {
@@ -10126,6 +10128,7 @@
b2.expectBroadcast();
VMSHandlerThread.quitSafely();
+ VMSHandlerThread.join();
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@@ -16974,6 +16977,7 @@
} finally {
cellFactory.terminate();
handlerThread.quitSafely();
+ handlerThread.join();
}
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 0b48e08..2ed989e 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -17,6 +17,8 @@
package com.android.server;
import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
+import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
@@ -54,7 +56,6 @@
import android.content.Context;
import android.net.INetd;
import android.net.Network;
-import android.net.connectivity.ConnectivityCompatChanges;
import android.net.mdns.aidl.DiscoveryInfo;
import android.net.mdns.aidl.GetAddressInfo;
import android.net.mdns.aidl.IMDnsEventListener;
@@ -190,7 +191,9 @@
}
@Test
- @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @DisableCompatChanges({
+ RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER,
+ ENABLE_PLATFORM_MDNS_BACKEND})
public void testPreSClients() throws Exception {
// Pre S client connected, the daemon should be started.
connectClient(mService);
@@ -217,7 +220,8 @@
}
@Test
- @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testNoDaemonStartedWhenClientsConnect() throws Exception {
// Creating an NsdManager will not cause daemon startup.
connectClient(mService);
@@ -251,7 +255,8 @@
}
@Test
- @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testClientRequestsAreGCedAtDisconnection() throws Exception {
final NsdManager client = connectClient(mService);
final INsdManagerCallback cb1 = getCallback();
@@ -294,7 +299,8 @@
}
@Test
- @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER)
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testCleanupDelayNoRequestActive() throws Exception {
final NsdManager client = connectClient(mService);
@@ -330,6 +336,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testDiscoverOnTetheringDownstream() throws Exception {
final NsdManager client = connectClient(mService);
final int interfaceIdx = 123;
@@ -420,6 +427,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testDiscoverOnBlackholeNetwork() throws Exception {
final NsdManager client = connectClient(mService);
final DiscoveryListener discListener = mock(DiscoveryListener.class);
@@ -449,6 +457,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testServiceRegistrationSuccessfulAndFailed() throws Exception {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -495,6 +504,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testServiceDiscoveryFailed() throws Exception {
final NsdManager client = connectClient(mService);
final DiscoveryListener discListener = mock(DiscoveryListener.class);
@@ -521,6 +531,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testServiceResolutionFailed() throws Exception {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -551,6 +562,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testGettingAddressFailed() throws Exception {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -597,6 +609,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testNoCrashWhenProcessResolutionAfterBinderDied() throws Exception {
final NsdManager client = connectClient(mService);
final INsdManagerCallback cb = getCallback();
@@ -616,6 +629,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testStopServiceResolution() {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -638,6 +652,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testStopResolutionFailed() {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -662,6 +677,7 @@
}
@Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testStopResolutionDuringGettingAddress() throws RemoteException {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
@@ -823,6 +839,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testMdnsDiscoveryManagerFeature() {
// Create NsdService w/o feature enabled.
final NsdManager client = connectClient(mService);
@@ -1012,6 +1029,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testMdnsAdvertiserFeatureFlagging() {
// Create NsdService w/o feature enabled.
final NsdManager client = connectClient(mService);
@@ -1047,6 +1065,7 @@
}
@Test
+ @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testTypeSpecificFeatureFlagging() {
doReturn("_type1._tcp:flag1,_type2._tcp:flag2").when(mDeps).getTypeAllowlistFlags();
doReturn(true).when(mDeps).isFeatureEnabled(any(),
@@ -1234,6 +1253,37 @@
assertEquals("_TEST._sub._999._tcp", constructServiceType(serviceType4));
}
+ @Test
+ @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
+ public void testEnablePlatformMdnsBackend() {
+ final NsdManager client = connectClient(mService);
+ final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE);
+ final Network network = new Network(999);
+ regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
+ regInfo.setPort(12345);
+ regInfo.setAttribute("testattr", "testvalue");
+ regInfo.setNetwork(network);
+
+ // Verify the registration uses MdnsAdvertiser
+ final RegistrationListener regListener = mock(RegistrationListener.class);
+ client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
+ waitForIdle();
+ verify(mSocketProvider).startMonitoringSockets();
+ verify(mAdvertiser).addService(anyInt(), any());
+
+ // Verify the discovery uses MdnsDiscoveryManager
+ final DiscoveryListener discListener = mock(DiscoveryListener.class);
+ client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener);
+ waitForIdle();
+ verify(mDiscoveryManager).registerListener(anyString(), any(), any());
+
+ // Verify the discovery uses MdnsDiscoveryManager
+ final ResolveListener resolveListener = mock(ResolveListener.class);
+ client.resolveService(regInfo, r -> r.run(), resolveListener);
+ waitForIdle();
+ verify(mDiscoveryManager, times(2)).registerListener(anyString(), any(), any());
+ }
+
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
}
@@ -1241,7 +1291,8 @@
NsdService makeService() {
final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) {
@Override
- public INsdServiceConnector connect(INsdManagerCallback baseCb) {
+ public INsdServiceConnector connect(INsdManagerCallback baseCb,
+ boolean runNewMdnsBackend) {
// Wrap the callback in a transparent mock, to mock asBinder returning a
// LinkToDeathRecorder. This will allow recording the binder death recipient
// registered on the callback. Use a transparent mock and not a spy as the actual
@@ -1250,7 +1301,7 @@
AdditionalAnswers.delegatesTo(baseCb));
doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
mCreatedCallbacks.add(cb);
- return super.connect(cb);
+ return super.connect(cb, runNewMdnsBackend);
}
};
return service;
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 1f965d9..79987e6 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -35,15 +35,24 @@
import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;
import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE;
import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_NONE;
+import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_UDP;
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;
+import static android.telephony.CarrierConfigManager.KEY_PREFERRED_IKE_PROTOCOL_INT;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import static com.android.server.connectivity.Vpn.AUTOMATIC_KEEPALIVE_DELAY_SECONDS;
import static com.android.server.connectivity.Vpn.DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_AUTO;
+import static com.android.server.connectivity.Vpn.PREFERRED_IKE_PROTOCOL_IPV4_UDP;
+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;
@@ -277,6 +286,7 @@
private static final int IKE_NATT_KEEPALIVE_DELAY_SEC_DEFAULT = 10;
private static final int TEST_KEEPALIVE_TIMER = 800;
private static final int TEST_SUB_ID = 1234;
+ private static final String TEST_MCCMNC = "12345";
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext;
@Mock private UserManager mUserManager;
@@ -1945,43 +1955,54 @@
}
@Test
- public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerNoTimer()
- throws Exception {
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerNoTimer() throws Exception {
doTestMigrateIkeSession_FromIkeTunnConnParams(
false /* isAutomaticIpVersionSelectionEnabled */,
true /* isAutomaticNattKeepaliveTimerEnabled */,
- TEST_KEEPALIVE_TIMEOUT_UNSET);
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
}
@Test
- public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerTimerSet()
- throws Exception {
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoTimerTimerSet() throws Exception {
doTestMigrateIkeSession_FromIkeTunnConnParams(
false /* isAutomaticIpVersionSelectionEnabled */,
true /* isAutomaticNattKeepaliveTimerEnabled */,
- TEST_KEEPALIVE_TIMER);
+ TEST_KEEPALIVE_TIMER /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
}
@Test
- public void testMigrateIkeSession_FromIkeTunnConnParams_AutoIp()
- throws Exception {
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AutoIp() throws Exception {
doTestMigrateIkeSession_FromIkeTunnConnParams(
true /* isAutomaticIpVersionSelectionEnabled */,
false /* isAutomaticNattKeepaliveTimerEnabled */,
- TEST_KEEPALIVE_TIMEOUT_UNSET);
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_AUTO /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_AUTO /* encapTypeInProfile */);
}
@Test
- public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoTimer()
- throws Exception {
+ public void testMigrateIkeSession_FromIkeTunnConnParams_AssignedIpProtocol() throws Exception {
+ doTestMigrateIkeSession_FromIkeTunnConnParams(
+ false /* isAutomaticIpVersionSelectionEnabled */,
+ false /* isAutomaticNattKeepaliveTimerEnabled */,
+ TEST_KEEPALIVE_TIMEOUT_UNSET /* keepaliveInProfile */,
+ ESP_IP_VERSION_IPV4 /* ipVersionInProfile */,
+ ESP_ENCAP_TYPE_UDP /* encapTypeInProfile */);
+ }
+
+ @Test
+ public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoTimer() throws Exception {
doTestMigrateIkeSession_FromNotIkeTunnConnParams(
false /* isAutomaticIpVersionSelectionEnabled */,
true /* isAutomaticNattKeepaliveTimerEnabled */);
}
@Test
- public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoIp()
- throws Exception {
+ public void testMigrateIkeSession_FromNotIkeTunnConnParams_AutoIp() throws Exception {
doTestMigrateIkeSession_FromNotIkeTunnConnParams(
true /* isAutomaticIpVersionSelectionEnabled */,
false /* isAutomaticNattKeepaliveTimerEnabled */);
@@ -2001,16 +2022,27 @@
final int expectedKeepalive = isAutomaticNattKeepaliveTimerEnabled
? AUTOMATIC_KEEPALIVE_DELAY_SECONDS
: DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
- doTestMigrateIkeSession(ikeProfile.toVpnProfile(), expectedKeepalive,
- isAutomaticIpVersionSelectionEnabled);
+ doTestMigrateIkeSession(ikeProfile.toVpnProfile(),
+ expectedKeepalive,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
}
private void doTestMigrateIkeSession_FromIkeTunnConnParams(
boolean isAutomaticIpVersionSelectionEnabled,
boolean isAutomaticNattKeepaliveTimerEnabled,
- int keepaliveInProfile) throws Exception {
- final IkeSessionParams ikeSessionParams = getTestIkeSessionParams(true /* testIpv6 */,
+ int keepaliveInProfile,
+ int ipVersionInProfile,
+ int encapTypeInProfile) throws Exception {
+ // 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 */,
new IkeFqdnIdentification(TEST_IDENTITY), keepaliveInProfile);
+ final IkeSessionParams ikeSessionParams = new IkeSessionParams.Builder(params)
+ .setIpVersion(ipVersionInProfile)
+ .setEncapType(encapTypeInProfile)
+ .build();
+
final IkeTunnelConnectionParams tunnelParams =
new IkeTunnelConnectionParams(ikeSessionParams, CHILD_PARAMS);
final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams)
@@ -2022,17 +2054,18 @@
final int expectedKeepalive = isAutomaticNattKeepaliveTimerEnabled
? AUTOMATIC_KEEPALIVE_DELAY_SECONDS
: ikeSessionParams.getNattKeepAliveDelaySeconds();
+ final int expectedIpVersion = isAutomaticIpVersionSelectionEnabled
+ ? ESP_IP_VERSION_AUTO
+ : ikeSessionParams.getIpVersion();
+ final int expectedEncapType = isAutomaticIpVersionSelectionEnabled
+ ? ESP_ENCAP_TYPE_AUTO
+ : ikeSessionParams.getEncapType();
doTestMigrateIkeSession(ikeProfile.toVpnProfile(), expectedKeepalive,
- isAutomaticIpVersionSelectionEnabled);
+ expectedIpVersion, expectedEncapType);
}
- private void doTestMigrateIkeSession(VpnProfile profile, int expectedKeepalive,
- boolean isAutomaticIpVersionSelectionEnabled) throws Exception {
- final int expectedIpVersion = isAutomaticIpVersionSelectionEnabled
- ? ESP_IP_VERSION_AUTO : ESP_IP_VERSION_AUTO;
- final int expectedEncapType = isAutomaticIpVersionSelectionEnabled
- ? ESP_ENCAP_TYPE_AUTO : ESP_IP_VERSION_AUTO;
-
+ private void doTestMigrateIkeSession(VpnProfile profile,
+ int expectedKeepalive, int expectedIpVersion, int expectedEncapType) throws Exception {
final PlatformVpnSnapshot vpnSnapShot =
verifySetupPlatformVpn(profile,
createIkeConfig(createIkeConnectInfo(), true /* isMobikeEnabled */),
@@ -2050,16 +2083,18 @@
vpnSnapShot.vpn.mVpnRunner.exitVpnRunner();
}
- private void mockCarrierConfig(int subId, int keepaliveTimer, int simStatus) {
+ private void mockCarrierConfig(int subId, int simStatus, int keepaliveTimer, int ikeProtocol) {
final SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
doReturn(subId).when(subscriptionInfo).getSubscriptionId();
doReturn(List.of(subscriptionInfo)).when(mSubscriptionManager)
.getActiveSubscriptionInfoList();
doReturn(simStatus).when(mTmPerSub).getSimApplicationState();
+ doReturn(TEST_MCCMNC).when(mTmPerSub).getSimOperator(subId);
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, keepaliveTimer);
+ persistableBundle.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, ikeProtocol);
// For CarrierConfigManager.isConfigForIdentifiedCarrier check
persistableBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
doReturn(persistableBundle).when(mConfigManager).getConfigForSubId(subId);
@@ -2076,26 +2111,32 @@
@Test
public void testNattKeepaliveTimerFromCarrierConfig_noSubId() throws Exception {
- doTestNattKeepaliveTimerFromCarrierConfig(new NetworkCapabilities(),
- TelephonyManager.SIM_STATE_LOADED, AUTOMATIC_KEEPALIVE_DELAY_SECONDS);
+ doTestReadCarrierConfig(new NetworkCapabilities(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
}
@Test
public void testNattKeepaliveTimerFromCarrierConfig_simAbsent() throws Exception {
- doTestNattKeepaliveTimerFromCarrierConfig(new NetworkCapabilities.Builder().build(),
- TelephonyManager.SIM_STATE_ABSENT, AUTOMATIC_KEEPALIVE_DELAY_SECONDS);
+ doTestReadCarrierConfig(new NetworkCapabilities.Builder().build(),
+ TelephonyManager.SIM_STATE_ABSENT,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
}
@Test
public void testNattKeepaliveTimerFromCarrierConfig() throws Exception {
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(TEST_SUB_ID)
- .build())
- .build();
- doTestNattKeepaliveTimerFromCarrierConfig(nc,
- TelephonyManager.SIM_STATE_LOADED, TEST_KEEPALIVE_TIMER);
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_AUTO,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
}
@Test
@@ -2104,17 +2145,62 @@
.addTransportType(TRANSPORT_WIFI)
.setTransportInfo(new WifiInfo.Builder().build())
.build();
- doTestNattKeepaliveTimerFromCarrierConfig(nc,
- TelephonyManager.SIM_STATE_LOADED, AUTOMATIC_KEEPALIVE_DELAY_SECONDS);
+ doTestReadCarrierConfig(nc,
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ AUTOMATIC_KEEPALIVE_DELAY_SECONDS /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_AUTO /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_AUTO /* expectedEncapType */);
}
- private void doTestNattKeepaliveTimerFromCarrierConfig(NetworkCapabilities nc, int simState,
- int expectedKeepaliveTimer) throws Exception {
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v4UDP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV4_UDP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV4 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_UDP /* expectedEncapType */);
+ }
+
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v6ESP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV6_ESP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_NONE /* expectedEncapType */);
+ }
+
+ @Test
+ public void testPreferredIpProtocolFromCarrierConfig_v6UDP() throws Exception {
+ doTestReadCarrierConfig(createTestCellNc(),
+ TelephonyManager.SIM_STATE_LOADED,
+ PREFERRED_IKE_PROTOCOL_IPV6_UDP,
+ TEST_KEEPALIVE_TIMER /* expectedKeepaliveTimer */,
+ ESP_IP_VERSION_IPV6 /* expectedIpVersion */,
+ ESP_ENCAP_TYPE_UDP /* expectedEncapType */);
+ }
+
+ private NetworkCapabilities createTestCellNc() {
+ return new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUB_ID)
+ .build())
+ .build();
+ }
+
+ private void doTestReadCarrierConfig(NetworkCapabilities nc, int simState, int preferredIpProto,
+ int expectedKeepaliveTimer, int expectedIpVersion, int expectedEncapType)
+ throws Exception {
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 =
@@ -2131,10 +2217,10 @@
verify(mIkeSessionWrapper, never()).setNetwork(any(), anyInt(), anyInt(), anyInt());
reset(mIkeSessionWrapper);
- mockCarrierConfig(TEST_SUB_ID, TEST_KEEPALIVE_TIMER, simState);
+ mockCarrierConfig(TEST_SUB_ID, simState, TEST_KEEPALIVE_TIMER, preferredIpProto);
vpnSnapShot.nwCb.onCapabilitiesChanged(TEST_NETWORK_2, nc);
verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2,
- ESP_IP_VERSION_AUTO, ESP_ENCAP_TYPE_AUTO, expectedKeepaliveTimer);
+ expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
reset(mExecutor);
reset(mIkeSessionWrapper);
@@ -2143,7 +2229,7 @@
listener.onCarrierConfigChanged(1 /* logicalSlotIndex */, TEST_SUB_ID,
-1 /* carrierId */, -1 /* specificCarrierId */);
verify(mIkeSessionWrapper).setNetwork(TEST_NETWORK_2,
- ESP_IP_VERSION_AUTO, ESP_ENCAP_TYPE_AUTO, expectedKeepaliveTimer);
+ expectedIpVersion, expectedEncapType, expectedKeepaliveTimer);
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index 375c150..a917361 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -97,6 +97,7 @@
@After
fun tearDown() {
thread.quitSafely()
+ thread.join()
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
index 6c3f729..7c6cb3e 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAnnouncerTest.kt
@@ -63,6 +63,7 @@
@After
fun tearDown() {
thread.quitSafely()
+ thread.join()
}
private class TestAnnouncementInfo(
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 2d8d8f3..0ca0835 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsInterfaceAdvertiserTest.kt
@@ -136,6 +136,7 @@
@After
fun tearDown() {
thread.quitSafely()
+ thread.join()
}
@Test
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 a2dbbc6..2b5423b 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -68,6 +68,7 @@
@After
fun tearDown() {
thread.quitSafely()
+ thread.join()
}
private class TestProbeInfo(probeRecords: List<MdnsRecord>, private val delayMs: Long = 1L) :
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 5665091..44e0d08 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -79,6 +79,7 @@
@After
fun tearDown() {
thread.quitSafely()
+ thread.join()
}
@Test
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 a80c078..b0a1f18 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -156,16 +156,20 @@
+ "010001000000780004C0A8018A0000000000000000000000000000"
+ "000000");
- // MDNS record for name "testhost1" with an IPv4 address of 10.1.2.3
+ // MDNS record for name "testhost1" with an IPv4 address of 10.1.2.3. Also set cache flush bit
+ // for the records changed.
private static final byte[] DATAIN_IPV4_1 = HexDump.hexStringToByteArray(
"0974657374686f73743100000180010000007800040a010203");
- // MDNS record for name "testhost1" with an IPv4 address of 10.1.2.4
+ // MDNS record for name "testhost1" with an IPv4 address of 10.1.2.4. Also set cache flush bit
+ // for the records changed.
private static final byte[] DATAIN_IPV4_2 = HexDump.hexStringToByteArray(
"0974657374686f73743100000180010000007800040a010204");
- // MDNS record w/name "testhost1" & IPv6 address of aabb:ccdd:1122:3344:a0b0:c0d0:1020:3040
+ // MDNS record w/name "testhost1" & IPv6 address of aabb:ccdd:1122:3344:a0b0:c0d0:1020:3040.
+ // Also set cache flush bit for the records changed.
private static final byte[] DATAIN_IPV6_1 = HexDump.hexStringToByteArray(
"0974657374686f73743100001c8001000000780010aabbccdd11223344a0b0c0d010203040");
- // MDNS record w/name "testhost1" & IPv6 address of aabb:ccdd:1122:3344:a0b0:c0d0:1020:3030
+ // MDNS record w/name "testhost1" & IPv6 address of aabb:ccdd:1122:3344:a0b0:c0d0:1020:3030.
+ // Also set cache flush bit for the records changed.
private static final byte[] DATAIN_IPV6_2 = HexDump.hexStringToByteArray(
"0974657374686f73743100001c8001000000780010aabbccdd11223344a0b0c0d010203030");
// MDNS record w/name "test" & PTR to foo.bar.quxx
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 3e2ea35..5d58f5d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -449,8 +449,8 @@
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
SERVICE_TYPE_LABELS,
- /* ipv4Address= */ List.of(),
- /* ipv6Address= */ List.of(),
+ /* ipv4Addresses= */ List.of(),
+ /* ipv6Addresses= */ List.of(),
/* port= */ 0,
/* subTypes= */ List.of(),
Collections.emptyMap(),
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
index 5e7f0ff..e6aba22 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetTrackerTest.java
@@ -86,8 +86,9 @@
}
@After
- public void cleanUp() {
+ public void cleanUp() throws InterruptedException {
mHandlerThread.quitSafely();
+ mHandlerThread.join();
}
private void initMockResources() {
diff --git a/tests/unit/java/com/android/server/net/IpConfigStoreTest.java b/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
index 4adc999..dcf0f75 100644
--- a/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
+++ b/tests/unit/java/com/android/server/net/IpConfigStoreTest.java
@@ -36,7 +36,6 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
-import com.android.testutils.HandlerUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,7 +57,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class IpConfigStoreTest {
- private static final int TIMEOUT_MS = 2_000;
+ private static final int TIMEOUT_MS = 5_000;
private static final int KEY_CONFIG = 17;
private static final String IFACE_1 = "eth0";
private static final String IFACE_2 = "eth1";
@@ -139,6 +138,8 @@
}
@Override
public void quitHandlerThread(HandlerThread handlerThread) {
+ // Don't join in here, quitHandlerThread runs on the
+ // handler thread itself.
testHandlerThread.quitSafely();
}
};
@@ -155,7 +156,7 @@
final DelayedDiskWrite writer = new DelayedDiskWrite(dependencies);
final IpConfigStore store = new IpConfigStore(writer);
store.writeIpConfigurations(configFile.getPath(), expectedNetworks);
- HandlerUtils.waitForIdle(testHandlerThread, TIMEOUT_MS);
+ testHandlerThread.join();
// Read IP config from the file path.
final ArrayMap<String, IpConfiguration> actualNetworks =
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 8046263..04163fd 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -539,6 +539,7 @@
mService = null;
mHandlerThread.quitSafely();
+ mHandlerThread.join();
}
private void initWifiStats(NetworkStateSnapshot snapshot) throws Exception {