Merge "Add test for benchmarking NetworkStatsRecorder" into main
diff --git a/Cronet/OWNERS b/Cronet/OWNERS
index 62c5737..c24680e 100644
--- a/Cronet/OWNERS
+++ b/Cronet/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/Cronet/tests/OWNERS b/Cronet/tests/OWNERS
index acb6ee6..a35a789 100644
--- a/Cronet/tests/OWNERS
+++ b/Cronet/tests/OWNERS
@@ -1,7 +1,7 @@
# Bug component: 31808
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking_xts
# TODO: Temp ownership to develop cronet CTS
colibie@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt b/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
index 0885f4f..3a77097 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
@@ -33,7 +33,9 @@
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers
import org.junit.After
+import org.junit.AssumptionViolatedException
import org.junit.Before
+import org.junit.Ignore
import org.junit.runner.RunWith
private const val URL = "https://source.android.com"
@@ -58,11 +60,6 @@
@After
@Throws(Exception::class)
fun tearDown() {
- // cancel active requests to enable engine shutdown.
- stream?.let {
- it.cancel()
- callback.blockForDone()
- }
httpEngine.shutdown()
}
@@ -72,10 +69,21 @@
@Test
@Throws(Exception::class)
+ @Ignore("b/292298108 Re-enable and confirm non-flaky after SLO")
fun testBidirectionalStream_GetStream_CompletesSuccessfully() {
stream = createBidirectionalStreamBuilder(URL).setHttpMethod("GET").build()
stream!!.start()
- callback.assumeCallback(ResponseStep.ON_SUCCEEDED)
+ // We call to a real server and hence the server may not be reachable, cancel this stream
+ // and rethrow the exception before tearDown,
+ // otherwise shutdown would fail with active request error.
+ try {
+ callback.assumeCallback(ResponseStep.ON_SUCCEEDED)
+ } catch (e: AssumptionViolatedException) {
+ stream!!.cancel()
+ callback.blockForDone()
+ throw e
+ }
+
val info = callback.mResponseInfo
assumeOKStatusCode(info)
MatcherAssert.assertThat(
@@ -190,5 +198,4 @@
stream = builder.build()
assertThat(stream!!.isDelayRequestHeadersUntilFirstFlushEnabled()).isTrue()
}
-
}
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index 4e4251c..63905c8 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -38,7 +38,11 @@
// tests need to inherit the NetHttpTests manifest.
android_library {
name: "NetHttpTestsLibPreJarJar",
- static_libs: ["cronet_java_tests"],
+ static_libs: [
+ "cronet_aml_api_java",
+ "cronet_aml_java__testing",
+ "cronet_java_tests",
+ ],
sdk_version: "module_current",
min_sdk_version: "30",
}
@@ -51,7 +55,8 @@
static_libs: ["NetHttpTestsLibPreJarJar"],
jarjar_rules: ":net-http-test-jarjar-rules",
jni_libs: [
- "cronet_aml_components_cronet_android_cronet_tests__testing"
+ "cronet_aml_components_cronet_android_cronet__testing",
+ "cronet_aml_components_cronet_android_cronet_tests__testing",
],
test_suites: [
"general-tests",
diff --git a/OWNERS b/OWNERS
index 07a775e..649efda 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,4 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
-per-file **IpSec* = file:platform/frameworks/base:master:/services/core/java/com/android/server/vcn/OWNERS
\ No newline at end of file
+per-file **IpSec* = file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 0326bf2..dafbf99 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -180,6 +180,9 @@
},
{
"exclude-annotation": "com.android.testutils.ConnectivityModuleTest"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.DnsResolverModuleTest"
}
]
},
@@ -195,6 +198,9 @@
},
{
"exclude-annotation": "androidx.test.filters.RequiresDevice"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.DnsResolverModuleTest"
}
]
},
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 905b8fa..a104084 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -138,10 +138,11 @@
}
switch (proto) {
- case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
- case IPPROTO_UDP: // address means there is no need to update their checksums.
- case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
- case IPPROTO_ESP: // since there is never a checksum to update.
+ case IPPROTO_TCP: // For TCP, UDP & UDPLITE the checksum neutrality of the chosen
+ case IPPROTO_UDP: // IPv6 address means there is no need to update their checksums.
+ case IPPROTO_UDPLITE: //
+ case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
+ case IPPROTO_ESP: // since there is never a checksum to update.
break;
default: // do not know how to handle anything else
@@ -328,12 +329,13 @@
if (ip4->frag_off & ~htons(IP_DF)) return TC_ACT_PIPE;
switch (ip4->protocol) {
- case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
- case IPPROTO_GRE: // address means there is no need to update their checksums.
- case IPPROTO_ESP: // We do not need to bother looking at GRE/ESP headers,
- break; // since there is never a checksum to update.
+ case IPPROTO_TCP: // For TCP, UDP & UDPLITE the checksum neutrality of the chosen
+ case IPPROTO_UDPLITE: // IPv6 address means there is no need to update their checksums.
+ case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
+ case IPPROTO_ESP: // since there is never a checksum to update.
+ break;
- case IPPROTO_UDP: // See above comment, but must also have UDP header...
+ case IPPROTO_UDP: // See above comment, but must also have UDP header...
if (data + sizeof(*ip4) + sizeof(struct udphdr) > data_end) return TC_ACT_PIPE;
const struct udphdr* uh = (const struct udphdr*)(ip4 + 1);
// If IPv4/UDP checksum is 0 then fallback to clatd so it can calculate the
diff --git a/framework/Android.bp b/framework/Android.bp
index 123f02a..cab11ae 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -109,8 +109,8 @@
"framework-connectivity-defaults",
],
static_libs: [
- "cronet_aml_api_java",
- "cronet_aml_java",
+ "httpclient_api",
+ "httpclient_impl",
],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
@@ -125,12 +125,12 @@
java_defaults {
name: "CronetJavaDefaults",
- srcs: [":cronet_aml_api_sources"],
+ srcs: [":httpclient_api_sources"],
libs: [
"androidx.annotation_annotation",
],
impl_only_static_libs: [
- "cronet_aml_java",
+ "httpclient_impl",
],
}
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
index 90f55b3..8376963 100644
--- a/framework/src/android/net/LinkAddress.java
+++ b/framework/src/android/net/LinkAddress.java
@@ -37,6 +37,8 @@
import android.os.SystemClock;
import android.util.Pair;
+import com.android.net.module.util.ConnectivityUtils;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -146,11 +148,7 @@
* Per RFC 4193 section 8, fc00::/7 identifies these addresses.
*/
private boolean isIpv6ULA() {
- if (isIpv6()) {
- byte[] bytes = address.getAddress();
- return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
- }
- return false;
+ return ConnectivityUtils.isIPv6ULA(address);
}
/**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 92e9599..8e219a6 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -690,17 +690,10 @@
*/
public static final int NET_CAPABILITY_PRIORITIZE_BANDWIDTH = 35;
- private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
- private static final int ALL_VALID_CAPABILITIES;
- static {
- int caps = 0;
- for (int i = MIN_NET_CAPABILITY; i <= MAX_NET_CAPABILITY; ++i) {
- caps |= 1 << i;
- }
- ALL_VALID_CAPABILITIES = caps;
- }
+ // Set all bits up to the MAX_NET_CAPABILITY-th bit
+ private static final long ALL_VALID_CAPABILITIES = (2L << MAX_NET_CAPABILITY) - 1;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -2519,7 +2512,7 @@
}
private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) {
- return capability >= MIN_NET_CAPABILITY && capability <= MAX_NET_CAPABILITY;
+ return capability >= 0 && capability <= MAX_NET_CAPABILITY;
}
private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) {
diff --git a/remote_auth/OWNERS b/remote_auth/OWNERS
new file mode 100644
index 0000000..25a32b9
--- /dev/null
+++ b/remote_auth/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 1145231
+# Bug template url: http://b/new?component=1145231&template=1715387
+billyhuang@google.com
+boetger@google.com
+casbor@google.com
+derekjedral@google.com
+dlm@google.com
+igorzas@google.com
+jacobhobbie@google.com
+jasonsun@google.com
+jianbing@google.com
+jinjiechen@google.com
+justinmcclain@google.com
+salilr@google.com
diff --git a/remote_auth/README.md b/remote_auth/README.md
new file mode 100644
index 0000000..384fcf7
--- /dev/null
+++ b/remote_auth/README.md
@@ -0,0 +1,47 @@
+# RemoteAuth Mainline Module
+
+This directory contains code for the RemoteAuth module.
+
+## Directory Structure
+
+`framework`
+ - Contains client side APIs and AIDL files.
+
+`jni`
+ - JNI wrapper for invoking Android APIs from native code.
+
+`native`
+ - Native code implementation for RemoteAuth module services.
+
+`service`
+ - Server side implementation for RemoteAuth module services.
+
+`tests`
+ - Unit/Multi devices tests for RemoteAuth module (both Java and native code).
+
+## IDE setup
+
+### AIDEGen
+
+AIDEGen is deprecated, prefer ASfP [go/asfp](http://go/asfp)
+```sh
+$ source build/envsetup.sh && lunch <TARGET>
+$ cd packages/modules/Connectivity
+$ aidegen .
+# This will launch Intellij project for RemoteAuth module.
+```
+
+### ASfP
+
+See full instructions for ASfP at [go/asfp-getting-started](http://go/asfp-getting-started)
+
+## Build and Install
+
+```sh
+$ source build/envsetup.sh && lunch <TARGET>
+$ m com.google.android.tethering deapexer
+$ $ANDROID_BUILD_TOP/out/host/linux-x86/bin/deapexer decompress --input \
+ ${ANDROID_PRODUCT_OUT}/system/apex/com.google.android.tethering.capex \
+ --output /tmp/tethering.apex
+$ adb install -r /tmp/tethering.apex
+```
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
new file mode 100644
index 0000000..bcedbef
--- /dev/null
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.metrics;
+
+import static com.android.metrics.NetworkNsdReported.Builder;
+
+import android.stats.connectivity.MdnsQueryResult;
+import android.stats.connectivity.NsdEventType;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ConnectivityStatsLog;
+
+/**
+ * Class to record the NetworkNsdReported into statsd. Each client should create this class to
+ * report its data.
+ */
+public class NetworkNsdReportedMetrics {
+ // Whether this client is using legacy backend.
+ private final boolean mIsLegacy;
+ // The client id.
+ private final int mClientId;
+ private final Dependencies mDependencies;
+
+ public NetworkNsdReportedMetrics(boolean isLegacy, int clientId) {
+ this(isLegacy, clientId, new Dependencies());
+ }
+
+ @VisibleForTesting
+ NetworkNsdReportedMetrics(boolean isLegacy, int clientId, Dependencies dependencies) {
+ mIsLegacy = isLegacy;
+ mClientId = clientId;
+ mDependencies = dependencies;
+ }
+
+ /**
+ * Dependencies of NetworkNsdReportedMetrics, for injection in tests.
+ */
+ public static class Dependencies {
+
+ /**
+ * @see ConnectivityStatsLog
+ */
+ public void statsWrite(NetworkNsdReported event) {
+ ConnectivityStatsLog.write(ConnectivityStatsLog.NETWORK_NSD_REPORTED,
+ event.getIsLegacy(),
+ event.getClientId(),
+ event.getTransactionId(),
+ event.getIsKnownService(),
+ event.getType().getNumber(),
+ event.getEventDurationMillisec(),
+ event.getQueryResult().getNumber(),
+ event.getFoundServiceCount(),
+ event.getFoundCallbackCount(),
+ event.getLostCallbackCount(),
+ event.getRepliedRequestsCount());
+ }
+ }
+
+ private Builder makeReportedBuilder() {
+ final Builder builder = NetworkNsdReported.newBuilder();
+ builder.setIsLegacy(mIsLegacy);
+ builder.setClientId(mClientId);
+ return builder;
+ }
+
+ /**
+ * Report service registration succeeded metric data.
+ *
+ * @param transactionId The transaction id of service registration.
+ * @param durationMs The duration of service registration success.
+ */
+ public void reportServiceRegistrationSucceeded(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_REGISTER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTERED);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service registration failed metric data.
+ *
+ * @param transactionId The transaction id of service registration.
+ * @param durationMs The duration of service registration failed.
+ */
+ public void reportServiceRegistrationFailed(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_REGISTER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_REGISTRATION_FAILED);
+ builder.setEventDurationMillisec(durationMs);
+ mDependencies.statsWrite(builder.build());
+ }
+
+ /**
+ * Report service unregistration success metric data.
+ *
+ * @param transactionId The transaction id of service registration.
+ * @param durationMs The duration of service stayed registered.
+ */
+ public void reportServiceUnregistration(int transactionId, long durationMs) {
+ final Builder builder = makeReportedBuilder();
+ builder.setTransactionId(transactionId);
+ builder.setType(NsdEventType.NET_REGISTER);
+ builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_UNREGISTERED);
+ builder.setEventDurationMillisec(durationMs);
+ // TODO: Report repliedRequestsCount
+ mDependencies.statsWrite(builder.build());
+ }
+}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index b06e9cb..745c5bc 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -26,6 +26,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -68,6 +69,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.metrics.NetworkNsdReportedMetrics;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.InetAddressUtils;
@@ -160,6 +162,7 @@
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
+ private static final int NO_TRANSACTION = -1;
private static final SharedLog LOGGER = new SharedLog("serviceDiscovery");
private final Context mContext;
@@ -176,6 +179,8 @@
private final MdnsSocketProvider mMdnsSocketProvider;
@NonNull
private final MdnsAdvertiser mAdvertiser;
+ @NonNull
+ private final Clock mClock;
private final SharedLog mServiceLogs = LOGGER.forSubComponent(TAG);
// WARNING : Accessing these values in any thread is not safe, it must only be changed in the
// state machine thread. If change this outside state machine, it will need to introduce
@@ -528,8 +533,11 @@
try {
cb.asBinder().linkToDeath(arg.connector, 0);
final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
+ final NetworkNsdReportedMetrics metrics =
+ mDeps.makeNetworkNsdReportedMetrics(
+ !arg.useJavaBackend, (int) mClock.elapsedRealtime());
cInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
- mServiceLogs.forSubComponent(tag));
+ mServiceLogs.forSubComponent(tag), metrics);
mClients.put(arg.connector, cInfo);
} catch (RemoteException e) {
Log.w(TAG, "Client request id " + clientRequestId
@@ -565,7 +573,7 @@
case NsdManager.REGISTER_SERVICE:
cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
- cInfo.onRegisterServiceFailed(
+ cInfo.onRegisterServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
@@ -647,8 +655,8 @@
private void storeLegacyRequestMap(int clientRequestId, int transactionId,
ClientInfo clientInfo, int what) {
- clientInfo.mClientRequests.put(
- clientRequestId, new LegacyClientRequest(transactionId, what));
+ clientInfo.mClientRequests.put(clientRequestId, new LegacyClientRequest(
+ transactionId, what, mClock, mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
// Remove the cleanup event because here comes a new request.
cancelStop();
@@ -656,8 +664,8 @@
private void storeAdvertiserRequestMap(int clientRequestId, int transactionId,
ClientInfo clientInfo, @Nullable Network requestedNetwork) {
- clientInfo.mClientRequests.put(clientRequestId,
- new AdvertiserClientRequest(transactionId, requestedNetwork));
+ clientInfo.mClientRequests.put(clientRequestId, new AdvertiserClientRequest(
+ transactionId, requestedNetwork, mClock, mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
updateMulticastLock();
}
@@ -680,8 +688,9 @@
private void storeDiscoveryManagerRequestMap(int clientRequestId, int transactionId,
MdnsListener listener, ClientInfo clientInfo,
@Nullable Network requestedNetwork) {
- clientInfo.mClientRequests.put(clientRequestId,
- new DiscoveryManagerRequest(transactionId, listener, requestedNetwork));
+ clientInfo.mClientRequests.put(clientRequestId, new DiscoveryManagerRequest(
+ transactionId, listener, requestedNetwork, mClock,
+ mClock.elapsedRealtime()));
mTransactionIdToClientInfoMap.put(transactionId, clientInfo);
updateMulticastLock();
}
@@ -834,7 +843,7 @@
}
if (requestLimitReached(clientInfo)) {
- clientInfo.onRegisterServiceFailed(
+ clientInfo.onRegisterServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
@@ -850,8 +859,8 @@
|| useAdvertiserForType(registerServiceType)) {
if (registerServiceType == null) {
Log.e(TAG, "Invalid service type: " + serviceType);
- clientInfo.onRegisterServiceFailed(clientRequestId,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onRegisterServiceFailedImmediately(
+ clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
serviceInfo.setServiceType(registerServiceType);
@@ -878,7 +887,7 @@
// Return success after mDns reports success
} else {
unregisterService(transactionId);
- clientInfo.onRegisterServiceFailed(
+ clientInfo.onRegisterServiceFailedImmediately(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
}
@@ -910,10 +919,12 @@
// instead of looking at the flag value.
if (request instanceof AdvertiserClientRequest) {
mAdvertiser.removeService(transactionId);
- clientInfo.onUnregisterServiceSucceeded(clientRequestId);
+ clientInfo.onUnregisterServiceSucceeded(clientRequestId, transactionId,
+ request.calculateRequestDurationMs());
} else {
if (unregisterService(transactionId)) {
- clientInfo.onUnregisterServiceSucceeded(clientRequestId);
+ clientInfo.onUnregisterServiceSucceeded(clientRequestId,
+ transactionId, request.calculateRequestDurationMs());
} else {
clientInfo.onUnregisterServiceFailed(
clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
@@ -1177,12 +1188,18 @@
final RegistrationInfo info = (RegistrationInfo) obj;
final String name = info.serviceName;
servInfo = new NsdServiceInfo(name, null /* serviceType */);
- clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo);
+ final ClientRequest request =
+ clientInfo.mClientRequests.get(clientRequestId);
+ clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo,
+ transactionId, request.calculateRequestDurationMs());
break;
}
case IMDnsEventListener.SERVICE_REGISTRATION_FAILED:
- clientInfo.onRegisterServiceFailed(
- clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR);
+ final ClientRequest request =
+ clientInfo.mClientRequests.get(clientRequestId);
+ clientInfo.onRegisterServiceFailed(clientRequestId,
+ NsdManager.FAILURE_INTERNAL_ERROR, transactionId,
+ request.calculateRequestDurationMs());
break;
case IMDnsEventListener.SERVICE_RESOLVED: {
final ResolutionInfo info = (ResolutionInfo) obj;
@@ -1562,6 +1579,7 @@
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
+ mClock = deps.makeClock();
}
/**
@@ -1651,6 +1669,21 @@
public int getCallingUid() {
return Binder.getCallingUid();
}
+
+ /**
+ * @see NetworkNsdReportedMetrics
+ */
+ public NetworkNsdReportedMetrics makeNetworkNsdReportedMetrics(
+ boolean isLegacy, int clientId) {
+ return new NetworkNsdReportedMetrics(isLegacy, clientId);
+ }
+
+ /**
+ * @see MdnsUtils.Clock
+ */
+ public Clock makeClock() {
+ return new Clock();
+ }
}
/**
@@ -1751,7 +1784,9 @@
// onRegisterServiceSucceeded only has the service name in its info. This aligns with
// historical behavior.
final NsdServiceInfo cbInfo = new NsdServiceInfo(registeredInfo.getServiceName(), null);
- clientInfo.onRegisterServiceSucceeded(clientRequestId, cbInfo);
+ final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
+ clientInfo.onRegisterServiceSucceeded(
+ clientRequestId, cbInfo, transactionId, request.calculateRequestDurationMs());
}
@Override
@@ -1761,8 +1796,9 @@
final int clientRequestId = getClientRequestIdOrLog(clientInfo, transactionId);
if (clientRequestId < 0) return;
-
- clientInfo.onRegisterServiceFailed(clientRequestId, errorCode);
+ final ClientRequest request = clientInfo.mClientRequests.get(clientRequestId);
+ clientInfo.onRegisterServiceFailed(clientRequestId, errorCode, transactionId,
+ request.calculateRequestDurationMs());
}
private ClientInfo getClientInfoOrLog(int transactionId) {
@@ -2022,17 +2058,27 @@
private abstract static class ClientRequest {
private final int mTransactionId;
+ private final Clock mClock;
+ private final long mStartTimeMs;
- private ClientRequest(int transactionId) {
+ private ClientRequest(int transactionId, @NonNull Clock clock, long startTimeMs) {
mTransactionId = transactionId;
+ mClock = clock;
+ mStartTimeMs = startTimeMs;
+ }
+
+ public long calculateRequestDurationMs() {
+ final long stopTimeMs = mClock.elapsedRealtime();
+ return stopTimeMs - mStartTimeMs;
}
}
private static class LegacyClientRequest extends ClientRequest {
private final int mRequestCode;
- private LegacyClientRequest(int transactionId, int requestCode) {
- super(transactionId);
+ private LegacyClientRequest(int transactionId, int requestCode, @NonNull Clock clock,
+ long startTimeMs) {
+ super(transactionId, clock, startTimeMs);
mRequestCode = requestCode;
}
}
@@ -2041,8 +2087,9 @@
@Nullable
private final Network mRequestedNetwork;
- private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork) {
- super(transactionId);
+ private JavaBackendClientRequest(int transactionId, @Nullable Network requestedNetwork,
+ @NonNull Clock clock, long startTimeMs) {
+ super(transactionId, clock, startTimeMs);
mRequestedNetwork = requestedNetwork;
}
@@ -2053,8 +2100,9 @@
}
private static class AdvertiserClientRequest extends JavaBackendClientRequest {
- private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork) {
- super(transactionId, requestedNetwork);
+ private AdvertiserClientRequest(int transactionId, @Nullable Network requestedNetwork,
+ @NonNull Clock clock, long startTimeMs) {
+ super(transactionId, requestedNetwork, clock, startTimeMs);
}
}
@@ -2063,8 +2111,8 @@
private final MdnsListener mListener;
private DiscoveryManagerRequest(int transactionId, @NonNull MdnsListener listener,
- @Nullable Network requestedNetwork) {
- super(transactionId, requestedNetwork);
+ @Nullable Network requestedNetwork, @NonNull Clock clock, long startTimeMs) {
+ super(transactionId, requestedNetwork, clock, startTimeMs);
mListener = listener;
}
}
@@ -2087,14 +2135,17 @@
private final boolean mUseJavaBackend;
// Store client logs
private final SharedLog mClientLogs;
+ // Report the nsd metrics data
+ private final NetworkNsdReportedMetrics mMetrics;
private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
- SharedLog sharedLog) {
+ SharedLog sharedLog, NetworkNsdReportedMetrics metrics) {
mCb = cb;
mUid = uid;
mUseJavaBackend = useJavaBackend;
mClientLogs = sharedLog;
mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
+ mMetrics = metrics;
}
@Override
@@ -2154,6 +2205,8 @@
if (request instanceof AdvertiserClientRequest) {
mAdvertiser.removeService(transactionId);
+ mMetrics.reportServiceUnregistration(
+ transactionId, request.calculateRequestDurationMs());
continue;
}
@@ -2170,6 +2223,8 @@
break;
case NsdManager.REGISTER_SERVICE:
unregisterService(transactionId);
+ mMetrics.reportServiceUnregistration(
+ transactionId, request.calculateRequestDurationMs());
break;
default:
break;
@@ -2261,7 +2316,13 @@
}
}
- void onRegisterServiceFailed(int listenerKey, int error) {
+ void onRegisterServiceFailedImmediately(int listenerKey, int error) {
+ onRegisterServiceFailed(listenerKey, error, NO_TRANSACTION, 0 /* durationMs */);
+ }
+
+ void onRegisterServiceFailed(int listenerKey, int error, int transactionId,
+ long durationMs) {
+ mMetrics.reportServiceRegistrationFailed(transactionId, durationMs);
try {
mCb.onRegisterServiceFailed(listenerKey, error);
} catch (RemoteException e) {
@@ -2269,7 +2330,9 @@
}
}
- void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info, int transactionId,
+ long durationMs) {
+ mMetrics.reportServiceRegistrationSucceeded(transactionId, durationMs);
try {
mCb.onRegisterServiceSucceeded(listenerKey, info);
} catch (RemoteException e) {
@@ -2285,7 +2348,8 @@
}
}
- void onUnregisterServiceSucceeded(int listenerKey) {
+ void onUnregisterServiceSucceeded(int listenerKey, int transactionId, long durationMs) {
+ mMetrics.reportServiceUnregistration(transactionId, durationMs);
try {
mCb.onUnregisterServiceSucceeded(listenerKey);
} catch (RemoteException e) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 13f6dac..b7417ed 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.INVALID_TRANSACTION_ID;
+
import android.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
@@ -74,7 +76,7 @@
@NonNull
private final List<MdnsResponse> servicesToResolve;
@NonNull
- private final MdnsResponseDecoder.Clock clock;
+ private final MdnsUtils.Clock clock;
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
EnqueueMdnsQueryCallable(
@@ -88,7 +90,7 @@
boolean onlyUseIpv6OnIpv6OnlyNetworks,
boolean sendDiscoveryQueries,
@NonNull Collection<MdnsResponse> servicesToResolve,
- @NonNull MdnsResponseDecoder.Clock clock) {
+ @NonNull MdnsUtils.Clock clock) {
weakRequestSender = new WeakReference<>(requestSender);
this.packetWriter = packetWriter;
serviceTypeLabels = TextUtils.split(serviceType, "\\.");
@@ -102,6 +104,11 @@
this.clock = clock;
}
+ /**
+ * Call to execute the mdns query.
+ *
+ * @return The pair of transaction id and the subtypes for the query.
+ */
// Incompatible return type for override of Callable#call().
@SuppressWarnings("nullness:override.return.invalid")
@Override
@@ -109,7 +116,7 @@
try {
MdnsSocketClientBase requestSender = weakRequestSender.get();
if (requestSender == null) {
- return Pair.create(-1, new ArrayList<>());
+ return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
int numQuestions = 0;
@@ -156,7 +163,7 @@
if (numQuestions == 0) {
// No query to send
- return Pair.create(-1, new ArrayList<>());
+ return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
// Header.
@@ -195,7 +202,7 @@
} catch (IOException e) {
LOGGER.e(String.format("Failed to create mDNS packet for subtype: %s.",
TextUtils.join(",", subtypes)), e);
- return Pair.create(-1, new ArrayList<>());
+ return Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java b/service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java
index 0eebc61..161669b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/ExecutorProvider.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity.mdns;
+import android.annotation.NonNull;
import android.util.ArraySet;
import java.util.Set;
@@ -47,5 +48,17 @@
}
executor.shutdownNow();
}
+ serviceTypeClientSchedulerExecutors.clear();
+ }
+
+ /**
+ * Shutdown one executor service and remove the executor service from the set.
+ * @param executorService the executorService to be shutdown
+ */
+ public void shutdownExecutorService(@NonNull ScheduledExecutorService executorService) {
+ if (!executorService.isShutdown()) {
+ executorService.shutdownNow();
+ }
+ serviceTypeClientSchedulerExecutors.remove(executorService);
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
index 8cb3e96..d4aeacf 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsConfigs.java
@@ -50,10 +50,6 @@
return false;
}
- public static boolean useSessionIdToScheduleMdnsTask() {
- return true;
- }
-
public static long sleepTimeForSocketThreadMs() {
return 20_000L;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
index ce5f540..0c32cf1 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
@@ -16,18 +16,13 @@
package com.android.server.connectivity.mdns;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
/** mDNS-related constants. */
-@VisibleForTesting(visibility = PACKAGE)
public final class MdnsConstants {
public static final int MDNS_PORT = 5353;
// Flags word format is:
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index dfaec75..d55098c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -51,6 +51,7 @@
@NonNull private final PerSocketServiceTypeClients perSocketServiceTypeClients;
@NonNull private final Handler handler;
@Nullable private final HandlerThread handlerThread;
+ @NonNull private final MdnsServiceCache serviceCache;
private static class PerSocketServiceTypeClients {
private final ArrayMap<Pair<String, SocketKey>, MdnsServiceTypeClient> clients =
@@ -119,10 +120,12 @@
if (socketClient.getLooper() != null) {
this.handlerThread = null;
this.handler = new Handler(socketClient.getLooper());
+ this.serviceCache = new MdnsServiceCache(socketClient.getLooper());
} else {
this.handlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName());
this.handlerThread.start();
this.handler = new Handler(handlerThread.getLooper());
+ this.serviceCache = new MdnsServiceCache(handlerThread.getLooper());
}
}
@@ -201,6 +204,7 @@
if (serviceTypeClient == null) return;
// Notify all listeners that all services are removed from this socket.
serviceTypeClient.notifySocketDestroyed();
+ executorProvider.shutdownExecutorService(serviceTypeClient.getExecutor());
perSocketServiceTypeClients.remove(serviceTypeClient);
}
});
@@ -235,6 +239,7 @@
if (serviceTypeClient.stopSendAndReceive(listener)) {
// No listener is registered for the service type anymore, remove it from the list
// of the service type clients.
+ executorProvider.shutdownExecutorService(serviceTypeClient.getExecutor());
perSocketServiceTypeClients.remove(serviceTypeClient);
}
}
@@ -289,6 +294,6 @@
return new MdnsServiceTypeClient(
serviceType, socketClient,
executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey,
- sharedLog.forSubComponent(tag), handler.getLooper());
+ sharedLog.forSubComponent(tag), handler.getLooper(), serviceCache);
}
}
\ No newline at end of file
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 eff1880..a0a538e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
-import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -40,10 +39,10 @@
private final boolean allowMultipleSrvRecordsPerHost =
MdnsConfigs.allowMultipleSrvRecordsPerHost();
@Nullable private final String[] serviceType;
- private final Clock clock;
+ private final MdnsUtils.Clock clock;
/** Constructs a new decoder that will extract responses for the given service type. */
- public MdnsResponseDecoder(@NonNull Clock clock, @Nullable String[] serviceType) {
+ public MdnsResponseDecoder(@NonNull MdnsUtils.Clock clock, @Nullable String[] serviceType) {
this.clock = clock;
this.serviceType = serviceType;
}
@@ -330,10 +329,4 @@
}
return result == null ? List.of() : result;
}
-
- public static class Clock {
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
- }
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
index dc99e49..ec6af9b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
@@ -96,7 +96,14 @@
: Collections.emptyList();
}
- private MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses,
+ /**
+ * Find a matched response for given service name
+ *
+ * @param responses the responses to be searched.
+ * @param serviceName the target service name
+ * @return the response which matches the given service name or null if not found.
+ */
+ public static MdnsResponse findMatchedResponse(@NonNull List<MdnsResponse> responses,
@NonNull String serviceName) {
for (MdnsResponse response : responses) {
if (equalsIgnoreDnsCase(serviceName, response.getServiceInstanceName())) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index 7035c90..b5fd8a0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.MdnsServiceCache.findMatchedResponse;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import android.annotation.NonNull;
@@ -28,7 +30,6 @@
import android.util.ArraySet;
import android.util.Pair;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
@@ -38,12 +39,9 @@
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
/**
@@ -57,6 +55,7 @@
@VisibleForTesting
static final int EVENT_START_QUERYTASK = 1;
static final int EVENT_QUERY_RESULT = 2;
+ static final int INVALID_TRANSACTION_ID = -1;
private final String serviceType;
private final String[] serviceTypeLabels;
@@ -67,13 +66,15 @@
@NonNull private final SharedLog sharedLog;
@NonNull private final Handler handler;
@NonNull private final Dependencies dependencies;
+ /**
+ * The service caches for each socket. It should be accessed from looper thread only.
+ */
+ @NonNull private final MdnsServiceCache serviceCache;
private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners =
new ArrayMap<>();
- // TODO: change instanceNameToResponse to TreeMap with case insensitive comparator.
- private final Map<String, MdnsResponse> instanceNameToResponse = new HashMap<>();
private final boolean removeServiceAfterTtlExpires =
MdnsConfigs.removeServiceAfterTtlExpires();
- private final MdnsResponseDecoder.Clock clock;
+ private final Clock clock;
@Nullable private MdnsSearchOptions searchOptions;
@@ -92,6 +93,7 @@
}
@Override
+ @SuppressWarnings("FutureReturnValueIgnored")
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_START_QUERYTASK: {
@@ -107,16 +109,15 @@
break;
}
case EVENT_QUERY_RESULT: {
- final QuerySentResult sentResult = (QuerySentResult) msg.obj;
- if (MdnsConfigs.useSessionIdToScheduleMdnsTask()) {
- // In case that the task is not canceled successfully, use session ID to
- // check if this task should continue to schedule more.
- if (sentResult.taskArgs.sessionId != currentSessionId) {
- break;
- }
+ final QuerySentArguments sentResult = (QuerySentArguments) msg.obj;
+ // If a task is cancelled while the Executor is running it, EVENT_QUERY_RESULT
+ // will still be sent when it ends. So use session ID to check if this task
+ // should continue to schedule more.
+ if (sentResult.taskArgs.sessionId != currentSessionId) {
+ break;
}
- if ((sentResult.transactionId != -1)) {
+ if ((sentResult.transactionId != INVALID_TRANSACTION_ID)) {
for (int i = 0; i < listeners.size(); i++) {
listeners.keyAt(i).onDiscoveryQuerySent(
sentResult.subTypes, sentResult.transactionId);
@@ -190,9 +191,10 @@
@NonNull ScheduledExecutorService executor,
@NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog,
- @NonNull Looper looper) {
- this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), socketKey,
- sharedLog, looper, new Dependencies());
+ @NonNull Looper looper,
+ @NonNull MdnsServiceCache serviceCache) {
+ this(serviceType, socketClient, executor, new Clock(), socketKey, sharedLog, looper,
+ new Dependencies(), serviceCache);
}
@VisibleForTesting
@@ -200,11 +202,12 @@
@NonNull String serviceType,
@NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor,
- @NonNull MdnsResponseDecoder.Clock clock,
+ @NonNull Clock clock,
@NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog,
@NonNull Looper looper,
- @NonNull Dependencies dependencies) {
+ @NonNull Dependencies dependencies,
+ @NonNull MdnsServiceCache serviceCache) {
this.serviceType = serviceType;
this.socketClient = socketClient;
this.executor = executor;
@@ -215,6 +218,7 @@
this.sharedLog = sharedLog;
this.handler = new QueryTaskHandler(looper);
this.dependencies = dependencies;
+ this.serviceCache = serviceCache;
}
private static MdnsServiceInfo buildMdnsServiceInfoFromResponse(
@@ -274,6 +278,7 @@
* @param listener The {@link MdnsServiceBrowserListener} to register.
* @param searchOptions {@link MdnsSearchOptions} contains the list of subtypes to discover.
*/
+ @SuppressWarnings("FutureReturnValueIgnored")
public void startSendAndReceive(
@NonNull MdnsServiceBrowserListener listener,
@NonNull MdnsSearchOptions searchOptions) {
@@ -281,7 +286,8 @@
this.searchOptions = searchOptions;
boolean hadReply = false;
if (listeners.put(listener, searchOptions) == null) {
- for (MdnsResponse existingResponse : instanceNameToResponse.values()) {
+ for (MdnsResponse existingResponse :
+ serviceCache.getCachedServices(serviceType, socketKey)) {
if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
final MdnsServiceInfo info =
buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
@@ -323,6 +329,13 @@
}
}
+ /**
+ * Get the executor service.
+ */
+ public ScheduledExecutorService getExecutor() {
+ return executor;
+ }
+
private void removeScheduledTask() {
dependencies.removeMessages(handler, EVENT_START_QUERYTASK);
sharedLog.log("Remove EVENT_START_QUERYTASK"
@@ -377,11 +390,13 @@
ensureRunningOnHandlerThread(handler);
// Augment the list of current known responses, and generated responses for resolve
// requests if there is no known response
- final List<MdnsResponse> currentList = new ArrayList<>(instanceNameToResponse.values());
+ final List<MdnsResponse> cachedList =
+ serviceCache.getCachedServices(serviceType, socketKey);
+ final List<MdnsResponse> currentList = new ArrayList<>(cachedList);
List<MdnsResponse> additionalResponses = makeResponsesForResolve(socketKey);
for (MdnsResponse additionalResponse : additionalResponses) {
- if (!instanceNameToResponse.containsKey(
- additionalResponse.getServiceInstanceName())) {
+ if (findMatchedResponse(
+ cachedList, additionalResponse.getServiceInstanceName()) == null) {
currentList.add(additionalResponse);
}
}
@@ -393,16 +408,17 @@
final ArrayList<MdnsResponse> allResponses = augmentedResult.second;
for (MdnsResponse response : allResponses) {
+ final String serviceInstanceName = response.getServiceInstanceName();
if (modifiedResponse.contains(response)) {
if (response.isGoodbye()) {
- onGoodbyeReceived(response.getServiceInstanceName());
+ onGoodbyeReceived(serviceInstanceName);
} else {
onResponseModified(response);
}
- } else if (instanceNameToResponse.containsKey(response.getServiceInstanceName())) {
+ } else if (findMatchedResponse(cachedList, serviceInstanceName) != null) {
// If the response is not modified and already in the cache. The cache will
// need to be updated to refresh the last receipt time.
- instanceNameToResponse.put(response.getServiceInstanceName(), response);
+ serviceCache.addOrUpdateService(serviceType, socketKey, response);
}
}
if (dependencies.hasMessages(handler, EVENT_START_QUERYTASK)
@@ -431,7 +447,7 @@
/** Notify all services are removed because the socket is destroyed. */
public void notifySocketDestroyed() {
ensureRunningOnHandlerThread(handler);
- for (MdnsResponse response : instanceNameToResponse.values()) {
+ for (MdnsResponse response : serviceCache.getCachedServices(serviceType, socketKey)) {
final String name = response.getServiceInstanceName();
if (name == null) continue;
for (int i = 0; i < listeners.size(); i++) {
@@ -453,18 +469,18 @@
private void onResponseModified(@NonNull MdnsResponse response) {
final String serviceInstanceName = response.getServiceInstanceName();
final MdnsResponse currentResponse =
- instanceNameToResponse.get(serviceInstanceName);
+ serviceCache.getCachedService(serviceInstanceName, serviceType, socketKey);
boolean newServiceFound = false;
boolean serviceBecomesComplete = false;
if (currentResponse == null) {
newServiceFound = true;
if (serviceInstanceName != null) {
- instanceNameToResponse.put(serviceInstanceName, response);
+ serviceCache.addOrUpdateService(serviceType, socketKey, response);
}
} else {
boolean before = currentResponse.isComplete();
- instanceNameToResponse.put(serviceInstanceName, response);
+ serviceCache.addOrUpdateService(serviceType, socketKey, response);
boolean after = response.isComplete();
serviceBecomesComplete = !before && after;
}
@@ -497,7 +513,8 @@
}
private void onGoodbyeReceived(@Nullable String serviceInstanceName) {
- final MdnsResponse response = instanceNameToResponse.remove(serviceInstanceName);
+ final MdnsResponse response =
+ serviceCache.removeService(serviceInstanceName, serviceType, socketKey);
if (response == null) {
return;
}
@@ -527,145 +544,6 @@
return new MdnsPacketWriter(DEFAULT_MTU);
}
- // A configuration for the PeriodicalQueryTask that contains parameters to build a query packet.
- // Call to getConfigForNextRun returns a config that can be used to build the next query task.
- @VisibleForTesting
- static class QueryTaskConfig {
-
- private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
- (int) MdnsConfigs.initialTimeBetweenBurstsMs();
- private static final int TIME_BETWEEN_BURSTS_MS = (int) MdnsConfigs.timeBetweenBurstsMs();
- private static final int QUERIES_PER_BURST = (int) MdnsConfigs.queriesPerBurst();
- private static final int TIME_BETWEEN_QUERIES_IN_BURST_MS =
- (int) MdnsConfigs.timeBetweenQueriesInBurstMs();
- private static final int QUERIES_PER_BURST_PASSIVE_MODE =
- (int) MdnsConfigs.queriesPerBurstPassive();
- private static final int UNSIGNED_SHORT_MAX_VALUE = 65536;
- // The following fields are used by QueryTask so we need to test them.
- @VisibleForTesting
- final List<String> subtypes;
- private final boolean alwaysAskForUnicastResponse =
- MdnsConfigs.alwaysAskForUnicastResponseInEachBurst();
- private final boolean usePassiveMode;
- private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
- private final int numOfQueriesBeforeBackoff;
- @VisibleForTesting
- final int transactionId;
- @VisibleForTesting
- final boolean expectUnicastResponse;
- private final int queriesPerBurst;
- private final int timeBetweenBurstsInMs;
- private final int burstCounter;
- private final long delayUntilNextTaskWithoutBackoffMs;
- private final boolean isFirstBurst;
- private final long queryCount;
- @NonNull private final SocketKey socketKey;
-
-
- QueryTaskConfig(@NonNull QueryTaskConfig other, long queryCount, int transactionId,
- boolean expectUnicastResponse, boolean isFirstBurst, int burstCounter,
- int queriesPerBurst, int timeBetweenBurstsInMs,
- long delayUntilNextTaskWithoutBackoffMs) {
- this.subtypes = new ArrayList<>(other.subtypes);
- this.usePassiveMode = other.usePassiveMode;
- this.onlyUseIpv6OnIpv6OnlyNetworks = other.onlyUseIpv6OnIpv6OnlyNetworks;
- this.numOfQueriesBeforeBackoff = other.numOfQueriesBeforeBackoff;
- this.transactionId = transactionId;
- this.expectUnicastResponse = expectUnicastResponse;
- this.queriesPerBurst = queriesPerBurst;
- this.timeBetweenBurstsInMs = timeBetweenBurstsInMs;
- this.burstCounter = burstCounter;
- this.delayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
- this.isFirstBurst = isFirstBurst;
- this.queryCount = queryCount;
- this.socketKey = other.socketKey;
- }
- QueryTaskConfig(@NonNull Collection<String> subtypes,
- boolean usePassiveMode,
- boolean onlyUseIpv6OnIpv6OnlyNetworks,
- int numOfQueriesBeforeBackoff,
- @Nullable SocketKey socketKey) {
- this.usePassiveMode = usePassiveMode;
- this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
- this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff;
- this.subtypes = new ArrayList<>(subtypes);
- this.queriesPerBurst = QUERIES_PER_BURST;
- this.burstCounter = 0;
- this.transactionId = 1;
- this.expectUnicastResponse = true;
- this.isFirstBurst = true;
- // Config the scan frequency based on the scan mode.
- if (this.usePassiveMode) {
- // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
- // in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
- // queries.
- this.timeBetweenBurstsInMs = TIME_BETWEEN_BURSTS_MS;
- } else {
- // In active scan mode, sends a burst of QUERIES_PER_BURST queries,
- // TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
- // then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
- // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
- this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
- }
- this.socketKey = socketKey;
- this.queryCount = 0;
- this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
- }
-
- QueryTaskConfig getConfigForNextRun() {
- long newQueryCount = queryCount + 1;
- int newTransactionId = transactionId + 1;
- if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
- newTransactionId = 1;
- }
- boolean newExpectUnicastResponse = false;
- boolean newIsFirstBurst = isFirstBurst;
- int newQueriesPerBurst = queriesPerBurst;
- int newBurstCounter = burstCounter + 1;
- long newDelayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
- int newTimeBetweenBurstsInMs = timeBetweenBurstsInMs;
- // Only the first query expects uni-cast response.
- if (newBurstCounter == queriesPerBurst) {
- newBurstCounter = 0;
-
- if (alwaysAskForUnicastResponse) {
- newExpectUnicastResponse = true;
- }
- // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and
- // then in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
- // queries.
- if (isFirstBurst) {
- newIsFirstBurst = false;
- if (usePassiveMode) {
- newQueriesPerBurst = QUERIES_PER_BURST_PASSIVE_MODE;
- }
- }
- // In active scan mode, sends a burst of QUERIES_PER_BURST queries,
- // TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
- // then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
- // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
- newDelayUntilNextTaskWithoutBackoffMs = timeBetweenBurstsInMs;
- if (timeBetweenBurstsInMs < TIME_BETWEEN_BURSTS_MS) {
- newTimeBetweenBurstsInMs = Math.min(timeBetweenBurstsInMs * 2,
- TIME_BETWEEN_BURSTS_MS);
- }
- } else {
- newDelayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
- }
- return new QueryTaskConfig(this, newQueryCount, newTransactionId,
- newExpectUnicastResponse, newIsFirstBurst, newBurstCounter, newQueriesPerBurst,
- newTimeBetweenBurstsInMs, newDelayUntilNextTaskWithoutBackoffMs);
- }
-
- private boolean shouldUseQueryBackoff() {
- // Don't enable backoff mode during the burst or in the first burst
- if (burstCounter != 0 || isFirstBurst) {
- return false;
- }
- return queryCount > numOfQueriesBeforeBackoff;
- }
- }
-
private List<MdnsResponse> makeResponsesForResolve(@NonNull SocketKey socketKey) {
final List<MdnsResponse> resolveResponses = new ArrayList<>();
for (int i = 0; i < listeners.size(); i++) {
@@ -673,7 +551,8 @@
if (resolveName == null) {
continue;
}
- MdnsResponse knownResponse = instanceNameToResponse.get(resolveName);
+ MdnsResponse knownResponse =
+ serviceCache.getCachedService(resolveName, serviceType, socketKey);
if (knownResponse == null) {
final ArrayList<String> instanceFullName = new ArrayList<>(
serviceTypeLabels.length + 1);
@@ -691,19 +570,21 @@
private void tryRemoveServiceAfterTtlExpires() {
if (!shouldRemoveServiceAfterTtlExpires()) return;
- Iterator<MdnsResponse> iter = instanceNameToResponse.values().iterator();
+ Iterator<MdnsResponse> iter =
+ serviceCache.getCachedServices(serviceType, socketKey).iterator();
while (iter.hasNext()) {
MdnsResponse existingResponse = iter.next();
+ final String serviceInstanceName = existingResponse.getServiceInstanceName();
if (existingResponse.hasServiceRecord()
&& existingResponse.getServiceRecord()
.getRemainingTTL(clock.elapsedRealtime()) == 0) {
- iter.remove();
+ serviceCache.removeService(serviceInstanceName, serviceType, socketKey);
for (int i = 0; i < listeners.size(); i++) {
if (!responseMatchesOptions(existingResponse, listeners.valueAt(i))) {
continue;
}
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
- if (existingResponse.getServiceInstanceName() != null) {
+ if (serviceInstanceName != null) {
final MdnsServiceInfo serviceInfo = buildMdnsServiceInfoFromResponse(
existingResponse, serviceTypeLabels);
if (existingResponse.isComplete()) {
@@ -733,12 +614,12 @@
}
}
- private static class QuerySentResult {
+ private static class QuerySentArguments {
private final int transactionId;
private final List<String> subTypes = new ArrayList<>();
private final ScheduledQueryTaskArgs taskArgs;
- QuerySentResult(int transactionId, @NonNull List<String> subTypes,
+ QuerySentArguments(int transactionId, @NonNull List<String> subTypes,
@NonNull ScheduledQueryTaskArgs taskArgs) {
this.transactionId = transactionId;
this.subTypes.addAll(subTypes);
@@ -781,11 +662,11 @@
} catch (RuntimeException e) {
sharedLog.e(String.format("Failed to run EnqueueMdnsQueryCallable for subtype: %s",
TextUtils.join(",", taskArgs.config.subtypes)), e);
- result = Pair.create(-1, new ArrayList<>());
+ result = Pair.create(INVALID_TRANSACTION_ID, new ArrayList<>());
}
dependencies.sendMessage(
handler, handler.obtainMessage(EVENT_QUERY_RESULT,
- new QuerySentResult(result.first, result.second, taskArgs)));
+ new QuerySentArguments(result.first, result.second, taskArgs)));
}
}
@@ -812,7 +693,7 @@
private long getMinRemainingTtl(long now) {
long minRemainingTtl = Long.MAX_VALUE;
- for (MdnsResponse response : instanceNameToResponse.values()) {
+ for (MdnsResponse response : serviceCache.getCachedServices(serviceType, socketKey)) {
if (!response.isComplete()) {
continue;
}
@@ -829,7 +710,6 @@
return minRemainingTtl == Long.MAX_VALUE ? 0 : minRemainingTtl;
}
- @GuardedBy("lock")
@NonNull
private void scheduleNextRun(@NonNull QueryTaskConfig nextRunConfig,
long minRemainingTtl,
diff --git a/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
new file mode 100644
index 0000000..19282b0
--- /dev/null
+++ b/service-t/src/com/android/server/connectivity/mdns/QueryTaskConfig.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A configuration for the PeriodicalQueryTask that contains parameters to build a query packet.
+ * Call to getConfigForNextRun returns a config that can be used to build the next query task.
+ */
+public class QueryTaskConfig {
+
+ private static final int INITIAL_TIME_BETWEEN_BURSTS_MS =
+ (int) MdnsConfigs.initialTimeBetweenBurstsMs();
+ private static final int TIME_BETWEEN_BURSTS_MS = (int) MdnsConfigs.timeBetweenBurstsMs();
+ private static final int QUERIES_PER_BURST = (int) MdnsConfigs.queriesPerBurst();
+ private static final int TIME_BETWEEN_QUERIES_IN_BURST_MS =
+ (int) MdnsConfigs.timeBetweenQueriesInBurstMs();
+ private static final int QUERIES_PER_BURST_PASSIVE_MODE =
+ (int) MdnsConfigs.queriesPerBurstPassive();
+ private static final int UNSIGNED_SHORT_MAX_VALUE = 65536;
+ // The following fields are used by QueryTask so we need to test them.
+ @VisibleForTesting
+ final List<String> subtypes;
+ private final boolean alwaysAskForUnicastResponse =
+ MdnsConfigs.alwaysAskForUnicastResponseInEachBurst();
+ private final boolean usePassiveMode;
+ final boolean onlyUseIpv6OnIpv6OnlyNetworks;
+ private final int numOfQueriesBeforeBackoff;
+ @VisibleForTesting
+ final int transactionId;
+ @VisibleForTesting
+ final boolean expectUnicastResponse;
+ private final int queriesPerBurst;
+ private final int timeBetweenBurstsInMs;
+ private final int burstCounter;
+ final long delayUntilNextTaskWithoutBackoffMs;
+ private final boolean isFirstBurst;
+ private final long queryCount;
+ @NonNull
+ final SocketKey socketKey;
+
+ QueryTaskConfig(@NonNull QueryTaskConfig other, long queryCount, int transactionId,
+ boolean expectUnicastResponse, boolean isFirstBurst, int burstCounter,
+ int queriesPerBurst, int timeBetweenBurstsInMs,
+ long delayUntilNextTaskWithoutBackoffMs) {
+ this.subtypes = new ArrayList<>(other.subtypes);
+ this.usePassiveMode = other.usePassiveMode;
+ this.onlyUseIpv6OnIpv6OnlyNetworks = other.onlyUseIpv6OnIpv6OnlyNetworks;
+ this.numOfQueriesBeforeBackoff = other.numOfQueriesBeforeBackoff;
+ this.transactionId = transactionId;
+ this.expectUnicastResponse = expectUnicastResponse;
+ this.queriesPerBurst = queriesPerBurst;
+ this.timeBetweenBurstsInMs = timeBetweenBurstsInMs;
+ this.burstCounter = burstCounter;
+ this.delayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
+ this.isFirstBurst = isFirstBurst;
+ this.queryCount = queryCount;
+ this.socketKey = other.socketKey;
+ }
+ QueryTaskConfig(@NonNull Collection<String> subtypes,
+ boolean usePassiveMode,
+ boolean onlyUseIpv6OnIpv6OnlyNetworks,
+ int numOfQueriesBeforeBackoff,
+ @Nullable SocketKey socketKey) {
+ this.usePassiveMode = usePassiveMode;
+ this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
+ this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff;
+ this.subtypes = new ArrayList<>(subtypes);
+ this.queriesPerBurst = QUERIES_PER_BURST;
+ this.burstCounter = 0;
+ this.transactionId = 1;
+ this.expectUnicastResponse = true;
+ this.isFirstBurst = true;
+ // Config the scan frequency based on the scan mode.
+ if (this.usePassiveMode) {
+ // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and then
+ // in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
+ // queries.
+ this.timeBetweenBurstsInMs = TIME_BETWEEN_BURSTS_MS;
+ } else {
+ // In active scan mode, sends a burst of QUERIES_PER_BURST queries,
+ // TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
+ // then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
+ // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
+ this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
+ }
+ this.socketKey = socketKey;
+ this.queryCount = 0;
+ this.delayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
+ }
+
+ /**
+ * Get new QueryTaskConfig for next run.
+ */
+ public QueryTaskConfig getConfigForNextRun() {
+ long newQueryCount = queryCount + 1;
+ int newTransactionId = transactionId + 1;
+ if (newTransactionId > UNSIGNED_SHORT_MAX_VALUE) {
+ newTransactionId = 1;
+ }
+ boolean newExpectUnicastResponse = false;
+ boolean newIsFirstBurst = isFirstBurst;
+ int newQueriesPerBurst = queriesPerBurst;
+ int newBurstCounter = burstCounter + 1;
+ long newDelayUntilNextTaskWithoutBackoffMs = delayUntilNextTaskWithoutBackoffMs;
+ int newTimeBetweenBurstsInMs = timeBetweenBurstsInMs;
+ // Only the first query expects uni-cast response.
+ if (newBurstCounter == queriesPerBurst) {
+ newBurstCounter = 0;
+
+ if (alwaysAskForUnicastResponse) {
+ newExpectUnicastResponse = true;
+ }
+ // In passive scan mode, sends a single burst of QUERIES_PER_BURST queries, and
+ // then in each TIME_BETWEEN_BURSTS interval, sends QUERIES_PER_BURST_PASSIVE_MODE
+ // queries.
+ if (isFirstBurst) {
+ newIsFirstBurst = false;
+ if (usePassiveMode) {
+ newQueriesPerBurst = QUERIES_PER_BURST_PASSIVE_MODE;
+ }
+ }
+ // In active scan mode, sends a burst of QUERIES_PER_BURST queries,
+ // TIME_BETWEEN_QUERIES_IN_BURST_MS apart, then waits for the scan interval, and
+ // then repeats. The scan interval starts as INITIAL_TIME_BETWEEN_BURSTS_MS and
+ // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
+ newDelayUntilNextTaskWithoutBackoffMs = timeBetweenBurstsInMs;
+ if (timeBetweenBurstsInMs < TIME_BETWEEN_BURSTS_MS) {
+ newTimeBetweenBurstsInMs = Math.min(timeBetweenBurstsInMs * 2,
+ TIME_BETWEEN_BURSTS_MS);
+ }
+ } else {
+ newDelayUntilNextTaskWithoutBackoffMs = TIME_BETWEEN_QUERIES_IN_BURST_MS;
+ }
+ return new QueryTaskConfig(this, newQueryCount, newTransactionId,
+ newExpectUnicastResponse, newIsFirstBurst, newBurstCounter, newQueriesPerBurst,
+ newTimeBetweenBurstsInMs, newDelayUntilNextTaskWithoutBackoffMs);
+ }
+
+ /**
+ * Determine if the query backoff should be used.
+ */
+ public boolean shouldUseQueryBackoff() {
+ // Don't enable backoff mode during the burst or in the first burst
+ if (burstCounter != 0 || isFirstBurst) {
+ return false;
+ }
+ return queryCount > numOfQueriesBeforeBackoff;
+ }
+}
diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
index 3180a6f..df3bde8 100644
--- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
+++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.net.Network;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.ArraySet;
import com.android.server.connectivity.mdns.MdnsConstants;
@@ -173,4 +174,14 @@
return mdnsRecord.getTtl() > 0
&& mdnsRecord.getRemainingTTL(now) <= mdnsRecord.getTtl() / 2;
}
+
+ /** A wrapper class of {@link SystemClock} to be mocked in unit tests. */
+ public static class Clock {
+ /**
+ * @see SystemClock#elapsedRealtime
+ */
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 1f22b02..8141350 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -431,7 +431,7 @@
for (String iface : getClientModeInterfaces(canUseRestrictedNetworks)) {
unicastInterfaceStateChange(listener, iface);
}
- if (mTetheringInterfaceMode == INTERFACE_MODE_SERVER) {
+ if (mTetheringInterface != null && mTetheringInterfaceMode == INTERFACE_MODE_SERVER) {
unicastInterfaceStateChange(listener, mTetheringInterface);
}
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index ec168dd..7aff6a4 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -40,6 +40,7 @@
import android.app.StatsManager;
import android.content.Context;
import android.net.INetd;
+import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.provider.DeviceConfig;
@@ -51,6 +52,8 @@
import android.util.Pair;
import android.util.StatsEvent;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.BackgroundThread;
import com.android.modules.utils.build.SdkLevel;
@@ -1140,19 +1143,48 @@
}
}
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private static native void native_init(boolean startSkDestroyListener);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_addNaughtyApp(int uid);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_removeNaughtyApp(int uid);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_addNiceApp(int uid);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_removeNiceApp(int uid);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_setChildChain(int childChain, boolean enable);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_replaceUidChain(String name, boolean isAllowlist, int[] uids);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_setUidRule(int childChain, int uid, int firewallRule);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_addUidInterfaceRules(String ifName, int[] uids);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_removeUidInterfaceRules(int[] uids);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_updateUidLockdownRule(int uid, boolean add);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native int native_swapActiveStatsMap();
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private native void native_setPermissionForUids(int permissions, int[] uids);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private static native void native_dump(FileDescriptor fd, boolean verbose);
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
private static native int native_synchronizeKernelRCU();
}
diff --git a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
index dde1d86..e5806a6 100644
--- a/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
+++ b/tests/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -28,6 +28,7 @@
import com.android.testutils.assertEqualBothWays
import com.android.testutils.assertParcelingIsLossless
import com.android.testutils.parcelingRoundTrip
+import java.net.Inet6Address
import java.net.InetAddress
import kotlin.test.assertFailsWith
import org.junit.Assert.assertEquals
@@ -44,10 +45,33 @@
private val TEST_PORT = 4243
private val TEST_PORT2 = 4244
+ // ::FFFF:1.2.3.4
+ private val SRC_V4_MAPPED_V6_ADDRESS_BYTES = byteArrayOf(
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0x00.toByte(),
+ 0xff.toByte(),
+ 0xff.toByte(),
+ 0x01.toByte(),
+ 0x02.toByte(),
+ 0x03.toByte(),
+ 0x04.toByte()
+ )
private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
private val TEST_DST_ADDRV4 = "198.168.0.1".address()
private val TEST_ADDRV6 = "2001:db8::1".address()
- private val TEST_ADDRV4MAPPEDV6 = "::ffff:1.2.3.4".address()
+ // This constant requires to be an Inet6Address, but InetAddresses.parseNumericAddress() will
+ // convert v4 mapped v6 address into an Inet4Address. So use Inet6Address.getByAddress() to
+ // create the address.
+ private val TEST_ADDRV4MAPPEDV6 = Inet6Address.getByAddress(null /* host */,
+ SRC_V4_MAPPED_V6_ADDRESS_BYTES, -1 /* scope_id */)
private val TEST_ADDRV4 = "1.2.3.4".address()
private fun String.address() = InetAddresses.parseNumericAddress(this)
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 8c2408b..286f9c8 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,7 +1,7 @@
# Bug template url: http://b/new?component=31808
# TODO: move bug template config to common owners file once b/226427845 is resolved
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking_xts
# IPsec
per-file **IpSec* = benedictwong@google.com, nharold@google.com
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
index a850e3b..7cac2af 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractExpeditedJobTest.java
@@ -74,6 +74,7 @@
@RequiredProperties({APP_STANDBY_MODE})
public void testNetworkAccess_appIdleState() throws Exception {
turnBatteryOn();
+ setAppIdle(false);
assertBackgroundNetworkAccess(true);
assertExpeditedJobHasNetworkAccess();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
index 78ae7b8..07434b1 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -64,6 +64,8 @@
"dumpsys usagestats appstandby",
"dumpsys connectivity trafficcontroller",
"dumpsys netd trafficcontroller",
+ "dumpsys platform_compat", // TODO (b/279829773): Remove this dump
+ "dumpsys jobscheduler " + TEST_APP2_PKG, // TODO (b/288220398): Remove this dump
}) {
dumpCommandOutput(out, cmd);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java
index 4266aad..35f1f1c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RestrictedModeTest.java
@@ -57,14 +57,18 @@
@Test
public void testNetworkAccess_withBatterySaver() throws Exception {
setBatterySaverMode(true);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
- setRestrictedNetworkingMode(true);
- // App would be denied network access since Restricted mode is on.
- assertBackgroundNetworkAccess(false);
- setRestrictedNetworkingMode(false);
- // Given that Restricted mode is turned off, app should be able to access network again.
- assertBackgroundNetworkAccess(true);
+ setRestrictedNetworkingMode(true);
+ // App would be denied network access since Restricted mode is on.
+ assertBackgroundNetworkAccess(false);
+ setRestrictedNetworkingMode(false);
+ // Given that Restricted mode is turned off, app should be able to access network again.
+ assertBackgroundNetworkAccess(true);
+ } finally {
+ setBatterySaverMode(false);
+ }
}
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
index d112425..69eba41 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -18,21 +18,24 @@
import android.platform.test.annotations.FlakyTest;
-import org.junit.After;
-import org.junit.Before;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+
import org.junit.Test;
public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
- @Before
- public void setUp() throws Exception {
- uninstallPackage(TEST_APP2_PKG, false);
- installPackage(TEST_APP2_APK);
+ @BeforeClassWithInfo
+ public static void setUpOnce(TestInformation testInfo) throws Exception {
+ uninstallPackage(testInfo, TEST_APP2_PKG, false);
+ installPackage(testInfo, TEST_APP2_APK);
}
- @After
- public void tearDown() throws Exception {
- uninstallPackage(TEST_APP2_PKG, true);
+ @AfterClassWithInfo
+ public static void tearDownOnce(TestInformation testInfo) throws DeviceNotAvailableException {
+ uninstallPackage(testInfo, TEST_APP2_PKG, true);
}
@Test
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index 566d9da..b89ab1f 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -46,7 +46,7 @@
protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
@BeforeClassWithInfo
- public static void setUpOnce(TestInformation testInfo) throws Exception {
+ public static void setUpOnceBase(TestInformation testInfo) throws Exception {
DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(testInfo.getDevice());
String testApk = deviceSdkLevel.isDeviceAtLeastT() ? TEST_APK_NEXT : TEST_APK;
@@ -55,7 +55,8 @@
}
@AfterClassWithInfo
- public static void tearDownOnce(TestInformation testInfo) throws DeviceNotAvailableException {
+ public static void tearDownOnceBase(TestInformation testInfo)
+ throws DeviceNotAvailableException {
uninstallPackage(testInfo, TEST_PKG, true);
}
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 3821cea..308aead 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -59,6 +59,7 @@
import com.android.net.module.util.DnsPacket;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DnsResolverModuleTest;
import com.android.testutils.SkipPresubmit;
import org.junit.After;
@@ -317,51 +318,61 @@
}
@Test
+ @DnsResolverModuleTest
public void testRawQuery() throws Exception {
doTestRawQuery(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryInline() throws Exception {
doTestRawQuery(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryBlob() throws Exception {
doTestRawQueryBlob(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryBlobInline() throws Exception {
doTestRawQueryBlob(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryRoot() throws Exception {
doTestRawQueryRoot(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryRootInline() throws Exception {
doTestRawQueryRoot(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryNXDomain() throws Exception {
doTestRawQueryNXDomain(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryNXDomainInline() throws Exception {
doTestRawQueryNXDomain(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryNXDomainWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutorInline);
}
@@ -610,41 +621,49 @@
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddress() throws Exception {
doTestQueryForInetAddress(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddressInline() throws Exception {
doTestQueryForInetAddress(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddressIpv4() throws Exception {
doTestQueryForInetAddressIpv4(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddressIpv4Inline() throws Exception {
doTestQueryForInetAddressIpv4(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddressIpv6() throws Exception {
doTestQueryForInetAddressIpv6(mExecutor);
}
@Test
+ @DnsResolverModuleTest
public void testQueryForInetAddressIpv6Inline() throws Exception {
doTestQueryForInetAddressIpv6(mExecutorInline);
}
@Test
+ @DnsResolverModuleTest
public void testContinuousQueries() throws Exception {
doTestContinuousQueries(mExecutor);
}
@Test
+ @DnsResolverModuleTest
@SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
public void testContinuousQueriesInline() throws Exception {
doTestContinuousQueries(mExecutorInline);
diff --git a/tests/native/connectivity_native_test/OWNERS b/tests/native/connectivity_native_test/OWNERS
index fbfcf92..c9bfc40 100644
--- a/tests/native/connectivity_native_test/OWNERS
+++ b/tests/native/connectivity_native_test/OWNERS
@@ -1,4 +1,4 @@
# Bug template url: http://b/new?component=31808
# TODO: move bug template config to common owners file once b/226427845 is resolved
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking_xts
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
new file mode 100644
index 0000000..961c422
--- /dev/null
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.metrics
+
+import android.os.Build
+import android.stats.connectivity.MdnsQueryResult
+import android.stats.connectivity.NsdEventType
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+class NetworkNsdReportedMetricsTest {
+ private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java)
+
+ @Test
+ fun testReportServiceRegistrationSucceeded() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceRegistrationSucceeded(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_REGISTER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_REGISTERED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceRegistrationFailed() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(false /* isLegacy */, clientId, deps)
+ metrics.reportServiceRegistrationFailed(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertFalse(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_REGISTER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_REGISTRATION_FAILED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+
+ @Test
+ fun testReportServiceUnregistration() {
+ val clientId = 99
+ val transactionId = 100
+ val durationMs = 10L
+ val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
+ metrics.reportServiceUnregistration(transactionId, durationMs)
+
+ val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
+ verify(deps).statsWrite(eventCaptor.capture())
+ eventCaptor.value.let {
+ assertTrue(it.isLegacy)
+ assertEquals(clientId, it.clientId)
+ assertEquals(transactionId, it.transactionId)
+ assertEquals(NsdEventType.NET_REGISTER, it.type)
+ assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, it.queryResult)
+ assertEquals(durationMs, it.eventDurationMillisec)
+ }
+ }
+}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index f51b28d..55384b3 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -43,7 +43,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
@@ -95,6 +97,7 @@
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.metrics.NetworkNsdReportedMetrics;
import com.android.server.NsdService.Dependencies;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
@@ -104,6 +107,7 @@
import com.android.server.connectivity.mdns.MdnsServiceInfo;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -138,6 +142,7 @@
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500;
+ private static final long TEST_TIME_MS = 123L;
private static final String SERVICE_NAME = "a_name";
private static final String SERVICE_TYPE = "_test._tcp";
private static final String SERVICE_FULL_NAME = SERVICE_NAME + "." + SERVICE_TYPE;
@@ -164,6 +169,8 @@
@Mock WifiManager mWifiManager;
@Mock WifiManager.MulticastLock mMulticastLock;
@Mock ActivityManager mActivityManager;
+ @Mock NetworkNsdReportedMetrics mMetrics;
+ @Mock MdnsUtils.Clock mClock;
SocketRequestMonitor mSocketRequestMonitor;
OnUidImportanceListener mUidImportanceListener;
HandlerThread mThread;
@@ -210,6 +217,9 @@
doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt(
eq(NsdService.MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF), anyInt());
doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any());
+ doReturn(mMetrics).when(mDeps).makeNetworkNsdReportedMetrics(anyBoolean(), anyInt());
+ doReturn(mClock).when(mDeps).makeClock();
+ doReturn(TEST_TIME_MS).when(mClock).elapsedRealtime();
mService = makeService();
final ArgumentCaptor<SocketRequestMonitor> cbMonitorCaptor =
ArgumentCaptor.forClass(SocketRequestMonitor.class);
@@ -512,14 +522,16 @@
eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
// Register service successfully.
+ final int regId = regIdCaptor.getValue();
final RegistrationInfo registrationInfo = new RegistrationInfo(
- regIdCaptor.getValue(),
+ regId,
IMDnsEventListener.SERVICE_REGISTERED,
SERVICE_NAME,
SERVICE_TYPE,
PORT,
new byte[0] /* txtRecord */,
IFACE_IDX_ANY);
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
eventListener.onServiceRegistrationStatus(registrationInfo);
final ArgumentCaptor<NsdServiceInfo> registeredInfoCaptor =
@@ -528,19 +540,22 @@
.onServiceRegistered(registeredInfoCaptor.capture());
final NsdServiceInfo registeredInfo = registeredInfoCaptor.getValue();
assertEquals(SERVICE_NAME, registeredInfo.getServiceName());
+ verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */);
// Fail to register service.
final RegistrationInfo registrationFailedInfo = new RegistrationInfo(
- regIdCaptor.getValue(),
+ regId,
IMDnsEventListener.SERVICE_REGISTRATION_FAILED,
null /* serviceName */,
null /* registrationType */,
0 /* port */,
new byte[0] /* txtRecord */,
IFACE_IDX_ANY);
+ doReturn(TEST_TIME_MS + 20L).when(mClock).elapsedRealtime();
eventListener.onServiceRegistrationStatus(registrationFailedInfo);
verify(regListener, timeout(TIMEOUT_MS))
.onRegistrationFailed(any(), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceRegistrationFailed(regId, 20L /* durationMs */);
}
@Test
@@ -1215,17 +1230,22 @@
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
- cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo);
+ final int regId = idCaptor.getValue();
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
+ cb.onRegisterServiceSucceeded(regId, regInfo);
verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(argThat(info -> matches(info,
new NsdServiceInfo(regInfo.getServiceName(), null))));
+ verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */);
+ doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime();
client.unregisterService(regListener);
waitForIdle();
verify(mAdvertiser).removeService(idCaptor.getValue());
verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
argThat(info -> matches(info, regInfo)));
verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive();
+ verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */);
}
@Test
@@ -1251,6 +1271,7 @@
verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed(
argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR));
+ verify(mMetrics).reportServiceRegistrationFailed(anyInt(), anyLong());
}
@Test
@@ -1280,10 +1301,13 @@
// Verify onServiceRegistered callback
final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue();
- cb.onRegisterServiceSucceeded(idCaptor.getValue(), regInfo);
+ final int regId = idCaptor.getValue();
+ doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime();
+ cb.onRegisterServiceSucceeded(regId, regInfo);
verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(
argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null))));
+ verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */);
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index b30c9ce..f4d3915 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -408,22 +408,22 @@
@Test
public void testIsAnyTcpSocketConnected_withTargetNetId() throws Exception {
setupResponseWithSocketExisting();
- mTestHandler.post(
- () -> assertTrue(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
+ assertTrue(visibleOnHandlerThread(mTestHandler,
+ () -> mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
}
@Test
public void testIsAnyTcpSocketConnected_withIncorrectNetId() throws Exception {
setupResponseWithSocketExisting();
- mTestHandler.post(
- () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(OTHER_NETID)));
+ assertFalse(visibleOnHandlerThread(mTestHandler,
+ () -> mAOOKeepaliveTracker.isAnyTcpSocketConnected(OTHER_NETID)));
}
@Test
public void testIsAnyTcpSocketConnected_noSocketExists() throws Exception {
setupResponseWithoutSocketExisting();
- mTestHandler.post(
- () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
+ assertFalse(visibleOnHandlerThread(mTestHandler,
+ () -> mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
}
private void triggerEventKeepalive(int slot, int reason) {
@@ -500,9 +500,7 @@
final AlarmManager.OnAlarmListener listener = listenerCaptor.getValue();
// For realism, the listener should be posted on the handler
- mTestHandler.post(() -> listener.onAlarm());
- // Wait for the listener to be called. The listener enqueues a message to the handler.
- HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+ visibleOnHandlerThread(mTestHandler, () -> listener.onAlarm());
// Wait for the message posted by the listener to be processed.
HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
@@ -525,8 +523,7 @@
doReturn(METRICS_COLLECTION_DURATION_MS).when(mDependencies).getElapsedRealtime();
// For realism, the listener should be posted on the handler
- mTestHandler.post(() -> listener.onAlarm());
- HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+ visibleOnHandlerThread(mTestHandler, () -> listener.onAlarm());
verify(mKeepaliveStatsTracker).writeAndResetMetrics();
// Alarm is rescheduled.
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 967083e..b319c30 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -50,6 +50,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -60,6 +61,7 @@
import android.telephony.TelephonyManager;
import android.testing.PollingCheck;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -428,6 +430,22 @@
// UiDevice.getLauncherPackageName() requires the test manifest to have a <queries> tag for
// the launcher intent.
+ // Attempted workaround for b/286550950 where Settings is reported as the launcher
+ PollingCheck.check(
+ "Launcher package name was still settings after " + TEST_TIMEOUT_MS + "ms",
+ TEST_TIMEOUT_MS,
+ () -> {
+ if ("com.android.settings".equals(uiDevice.getLauncherPackageName())) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ final List<ResolveInfo> acts = ctx.getPackageManager()
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ Log.e(NetworkNotificationManagerTest.class.getSimpleName(),
+ "Got settings as launcher name; launcher activities: " + acts);
+ return false;
+ }
+ return true;
+ });
final String launcherPackageName = uiDevice.getLauncherPackageName();
assertTrue(String.format("Launcher (%s) is not shown", launcherPackageName),
uiDevice.wait(Until.hasObject(By.pkg(launcherPackageName)),
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
index 1a4ae5d..e869b91 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -53,6 +53,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
/** Tests for {@link MdnsDiscoveryManager}. */
@RunWith(DevSdkIgnoreRunner.class)
@@ -80,6 +81,7 @@
private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_2_NETWORK_2 =
Pair.create(SERVICE_TYPE_2, SOCKET_KEY_NETWORK_2);
@Mock private ExecutorProvider executorProvider;
+ @Mock private ScheduledExecutorService mockExecutorService;
@Mock private MdnsSocketClientBase socketClient;
@Mock private MdnsServiceTypeClient mockServiceTypeClientType1NullNetwork;
@Mock private MdnsServiceTypeClient mockServiceTypeClientType1Network1;
@@ -128,6 +130,7 @@
return null;
}
};
+ doReturn(mockExecutorService).when(mockServiceTypeClientType1NullNetwork).getExecutor();
}
@After
@@ -165,11 +168,25 @@
when(mockServiceTypeClientType1NullNetwork.stopSendAndReceive(mockListenerOne))
.thenReturn(true);
runOnHandler(() -> discoveryManager.unregisterListener(SERVICE_TYPE_1, mockListenerOne));
+ verify(executorProvider).shutdownExecutorService(mockExecutorService);
verify(mockServiceTypeClientType1NullNetwork).stopSendAndReceive(mockListenerOne);
verify(socketClient).stopDiscovery();
}
@Test
+ public void onSocketDestroy_shutdownExecutorService() throws IOException {
+ final MdnsSearchOptions options =
+ MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
+ final SocketCreationCallback callback = expectSocketCreationCallback(
+ SERVICE_TYPE_1, mockListenerOne, options);
+ runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
+ verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options);
+
+ runOnHandler(() -> callback.onSocketDestroyed(SOCKET_KEY_NULL_NETWORK));
+ verify(executorProvider).shutdownExecutorService(mockExecutorService);
+ }
+
+ @Test
public void registerMultipleListeners() throws IOException {
final MdnsSearchOptions options =
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
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 05eca84..d71bea4 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -18,7 +18,7 @@
import static android.net.InetAddresses.parseNumericAddress;
-import static com.android.server.connectivity.mdns.MdnsResponseDecoder.Clock;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertArrayEquals;
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 4328053..1fdfe09 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -52,7 +52,7 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
-import com.android.server.connectivity.mdns.MdnsServiceTypeClient.QueryTaskConfig;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -92,6 +92,7 @@
private static final int INTERFACE_INDEX = 999;
private static final long DEFAULT_TIMEOUT = 2000L;
private static final String SERVICE_TYPE = "_googlecast._tcp.local";
+ private static final String SUBTYPE = "_subtype";
private static final String[] SERVICE_TYPE_LABELS = TextUtils.split(SERVICE_TYPE, "\\.");
private static final InetSocketAddress IPV4_ADDRESS = new InetSocketAddress(
MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
@@ -113,7 +114,7 @@
@Mock
private Network mockNetwork;
@Mock
- private MdnsResponseDecoder.Clock mockDecoderClock;
+ private MdnsUtils.Clock mockDecoderClock;
@Mock
private SharedLog mockSharedLog;
@Mock
@@ -131,6 +132,7 @@
private SocketKey socketKey;
private HandlerThread thread;
private Handler handler;
+ private MdnsServiceCache serviceCache;
private long latestDelayMs = 0;
private Message delayMessage = null;
private Handler realHandler = null;
@@ -190,6 +192,7 @@
thread = new HandlerThread("MdnsServiceTypeClientTests");
thread.start();
handler = new Handler(thread.getLooper());
+ serviceCache = new MdnsServiceCache(thread.getLooper());
doAnswer(inv -> {
latestDelayMs = 0;
@@ -213,7 +216,8 @@
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) {
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -258,7 +262,7 @@
@Test
public void sendQueries_activeScanMode() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(false).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
@@ -310,7 +314,7 @@
@Test
public void sendQueries_reentry_activeScanMode() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(false).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
@@ -321,8 +325,8 @@
// After the first query is sent, change the subtypes, and restart.
searchOptions =
MdnsSearchOptions.newBuilder()
- .addSubtype("12345")
- .addSubtype("abcde")
+ .addSubtype(SUBTYPE)
+ .addSubtype("_subtype2")
.setIsPassiveMode(false)
.build();
startSendAndReceive(mockListenerOne, searchOptions);
@@ -344,7 +348,7 @@
@Test
public void sendQueries_passiveScanMode() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(true).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(true).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
@@ -370,7 +374,7 @@
@Test
public void sendQueries_activeScanWithQueryBackoff() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(
false).setNumOfQueriesBeforeBackoff(11).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
@@ -429,7 +433,7 @@
@Test
public void sendQueries_passiveScanWithQueryBackoff() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(
true).setNumOfQueriesBeforeBackoff(3).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
@@ -488,7 +492,7 @@
@Test
public void sendQueries_reentry_passiveScanMode() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(true).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(true).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Always try to remove the task.
verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
@@ -499,8 +503,8 @@
// After the first query is sent, change the subtypes, and restart.
searchOptions =
MdnsSearchOptions.newBuilder()
- .addSubtype("12345")
- .addSubtype("abcde")
+ .addSubtype(SUBTYPE)
+ .addSubtype("_subtype2")
.setIsPassiveMode(true)
.build();
startSendAndReceive(mockListenerOne, searchOptions);
@@ -524,7 +528,7 @@
public void testQueryTaskConfig_alwaysAskForUnicastResponse() {
//MdnsConfigsFlagsImpl.alwaysAskForUnicastResponseInEachBurst.override(true);
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig(
searchOptions.getSubtypes(), searchOptions.isPassiveMode(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
@@ -555,7 +559,7 @@
@Test
public void testQueryTaskConfig_askForUnicastInFirstQuery() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig(
searchOptions.getSubtypes(), searchOptions.isPassiveMode(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
@@ -586,15 +590,15 @@
@Test
public void testIfPreviousTaskIsCanceledWhenNewSessionStarts() {
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(true).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(true).build();
startSendAndReceive(mockListenerOne, searchOptions);
Runnable firstMdnsTask = currentThreadExecutor.getAndClearSubmittedRunnable();
// Change the sutypes and start a new session.
searchOptions =
MdnsSearchOptions.newBuilder()
- .addSubtype("12345")
- .addSubtype("abcde")
+ .addSubtype(SUBTYPE)
+ .addSubtype("_subtype2")
.setIsPassiveMode(true)
.build();
startSendAndReceive(mockListenerOne, searchOptions);
@@ -615,7 +619,7 @@
public void testIfPreviousTaskIsCanceledWhenSessionStops() {
//MdnsConfigsFlagsImpl.shouldCancelScanTaskWhenFutureIsNull.override(true);
MdnsSearchOptions searchOptions =
- MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(true).build();
+ MdnsSearchOptions.newBuilder().addSubtype(SUBTYPE).setIsPassiveMode(true).build();
startSendAndReceive(mockListenerOne, searchOptions);
// Change the sutypes and start a new session.
stopSendAndReceive(mockListenerOne);
@@ -704,14 +708,12 @@
// Process the initial response.
processResponse(createResponse(
- "service-instance-1", ipV4Address, 5353,
- /* subtype= */ "ABCDE",
+ "service-instance-1", ipV4Address, 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response with a different port and updated text attributes.
processResponse(createResponse(
- "service-instance-1", ipV4Address, 5354,
- /* subtype= */ "ABCDE",
+ "service-instance-1", ipV4Address, 5354, SUBTYPE,
Collections.singletonMap("key", "value"), TEST_TTL),
socketKey);
@@ -723,7 +725,7 @@
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
- Collections.singletonList("ABCDE") /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
@@ -733,7 +735,7 @@
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv4Address(), ipV4Address);
assertEquals(initialServiceInfo.getPort(), 5353);
- assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
+ assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList(SUBTYPE));
assertNull(initialServiceInfo.getAttributeByKey("key"));
assertEquals(socketKey.getInterfaceIndex(), initialServiceInfo.getInterfaceIndex());
assertEquals(socketKey.getNetwork(), initialServiceInfo.getNetwork());
@@ -745,7 +747,7 @@
assertEquals(updatedServiceInfo.getIpv4Address(), ipV4Address);
assertEquals(updatedServiceInfo.getPort(), 5354);
assertTrue(updatedServiceInfo.hasSubtypes());
- assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
+ assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList(SUBTYPE));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
assertEquals(socketKey.getInterfaceIndex(), updatedServiceInfo.getInterfaceIndex());
assertEquals(socketKey.getNetwork(), updatedServiceInfo.getNetwork());
@@ -758,14 +760,12 @@
// Process the initial response.
processResponse(createResponse(
- "service-instance-1", ipV6Address, 5353,
- /* subtype= */ "ABCDE",
+ "service-instance-1", ipV6Address, 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response with a different port and updated text attributes.
processResponse(createResponse(
- "service-instance-1", ipV6Address, 5354,
- /* subtype= */ "ABCDE",
+ "service-instance-1", ipV6Address, 5354, SUBTYPE,
Collections.singletonMap("key", "value"), TEST_TTL),
socketKey);
@@ -777,7 +777,7 @@
List.of() /* ipv4Address */,
List.of(ipV6Address) /* ipv6Address */,
5353 /* port */,
- Collections.singletonList("ABCDE") /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
@@ -787,7 +787,7 @@
assertEquals(initialServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(initialServiceInfo.getIpv6Address(), ipV6Address);
assertEquals(initialServiceInfo.getPort(), 5353);
- assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
+ assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList(SUBTYPE));
assertNull(initialServiceInfo.getAttributeByKey("key"));
assertEquals(socketKey.getInterfaceIndex(), initialServiceInfo.getInterfaceIndex());
assertEquals(socketKey.getNetwork(), initialServiceInfo.getNetwork());
@@ -799,7 +799,7 @@
assertEquals(updatedServiceInfo.getIpv6Address(), ipV6Address);
assertEquals(updatedServiceInfo.getPort(), 5354);
assertTrue(updatedServiceInfo.hasSubtypes());
- assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
+ assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList(SUBTYPE));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
assertEquals(socketKey.getInterfaceIndex(), updatedServiceInfo.getInterfaceIndex());
assertEquals(socketKey.getNetwork(), updatedServiceInfo.getNetwork());
@@ -861,8 +861,7 @@
public void reportExistingServiceToNewlyRegisteredListeners() throws Exception {
// Process the initial response.
processResponse(createResponse(
- "service-instance-1", "192.168.1.1", 5353,
- /* subtype= */ "ABCDE",
+ "service-instance-1", "192.168.1.1", 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
@@ -875,7 +874,7 @@
List.of("192.168.1.1") /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
- Collections.singletonList("ABCDE") /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
@@ -885,7 +884,7 @@
assertEquals(existingServiceInfo.getServiceInstanceName(), "service-instance-1");
assertEquals(existingServiceInfo.getIpv4Address(), "192.168.1.1");
assertEquals(existingServiceInfo.getPort(), 5353);
- assertEquals(existingServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
+ assertEquals(existingServiceInfo.getSubtypes(), Collections.singletonList(SUBTYPE));
assertNull(existingServiceInfo.getAttributeByKey("key"));
// Process a goodbye message for the existing response.
@@ -908,7 +907,8 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) {
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -923,7 +923,7 @@
// Process the initial response.
processResponse(createResponse(
- serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
+ serviceInstanceName, "192.168.1.1", 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
@@ -953,7 +953,8 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) {
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -964,7 +965,7 @@
// Process the initial response.
processResponse(createResponse(
- serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
+ serviceInstanceName, "192.168.1.1", 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
@@ -986,7 +987,8 @@
final String serviceInstanceName = "service-instance-1";
client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps) {
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache) {
@Override
MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter;
@@ -997,7 +999,7 @@
// Process the initial response.
processResponse(createResponse(
- serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
+ serviceInstanceName, "192.168.1.1", 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
@@ -1021,19 +1023,18 @@
InOrder inOrder = inOrder(mockListenerOne);
// Process the initial response which is incomplete.
- final String subtype = "ABCDE";
processResponse(createResponse(
- serviceName, null, 5353, subtype,
+ serviceName, null, 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response which has ip address to make response become complete.
processResponse(createResponse(
- serviceName, ipV4Address, 5353, subtype,
+ serviceName, ipV4Address, 5353, SUBTYPE,
Collections.emptyMap(), TEST_TTL), socketKey);
// Process a third response with a different ip address, port and updated text attributes.
processResponse(createResponse(
- serviceName, ipV6Address, 5354, subtype,
+ serviceName, ipV6Address, 5354, SUBTYPE,
Collections.singletonMap("key", "value"), TEST_TTL), socketKey);
// Process the last response which is goodbye message (with the main type, not subtype).
@@ -1050,7 +1051,7 @@
List.of() /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
- Collections.singletonList(subtype) /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
@@ -1062,7 +1063,7 @@
List.of(ipV4Address) /* ipv4Address */,
List.of() /* ipv6Address */,
5353 /* port */,
- Collections.singletonList(subtype) /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
socketKey);
@@ -1074,7 +1075,7 @@
List.of(ipV4Address) /* ipv4Address */,
List.of(ipV6Address) /* ipv6Address */,
5354 /* port */,
- Collections.singletonList(subtype) /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
socketKey);
@@ -1086,7 +1087,7 @@
List.of(ipV4Address) /* ipv4Address */,
List.of(ipV6Address) /* ipv6Address */,
5354 /* port */,
- Collections.singletonList("ABCDE") /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
socketKey);
@@ -1098,7 +1099,7 @@
List.of(ipV4Address) /* ipv4Address */,
List.of(ipV6Address) /* ipv6Address */,
5354 /* port */,
- Collections.singletonList("ABCDE") /* subTypes */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
socketKey);
}
@@ -1106,7 +1107,8 @@
@Test
public void testProcessResponse_Resolve() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps);
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache);
final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "};
@@ -1199,7 +1201,8 @@
@Test
public void testRenewTxtSrvInResolve() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps);
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache);
final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "};
@@ -1312,7 +1315,8 @@
@Test
public void testProcessResponse_ResolveExcludesOtherServices() {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps);
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache);
final String requestedInstance = "instance1";
final String otherInstance = "instance2";
@@ -1376,7 +1380,8 @@
@Test
public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps);
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache);
final String matchingInstance = "instance1";
final String subtype = "_subtype";
@@ -1457,7 +1462,8 @@
@Test
public void testNotifySocketDestroyed() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
- mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps);
+ mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps,
+ serviceCache);
final String requestedInstance = "instance1";
final String otherInstance = "instance2";
@@ -1516,12 +1522,106 @@
inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(
matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
- inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
- inOrder2.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
- inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
- inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
inOrder2.verify(mockListenerTwo).onServiceRemoved(matchServiceName(requestedInstance));
inOrder2.verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(requestedInstance));
+ verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
+ verify(mockListenerTwo).onServiceNameRemoved(matchServiceName(otherInstance));
+ }
+
+ @Test
+ public void testServicesAreCached() throws Exception {
+ final String serviceName = "service-instance";
+ final String ipV4Address = "192.0.2.0";
+ // Register a listener
+ startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
+ verify(mockDeps, times(1)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
+ InOrder inOrder = inOrder(mockListenerOne);
+
+ // Process a response which has ip address to make response become complete.
+
+ processResponse(createResponse(
+ serviceName, ipV4Address, 5353, SUBTYPE,
+ Collections.emptyMap(), TEST_TTL),
+ socketKey);
+
+ // Verify that onServiceNameDiscovered is called.
+ inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
+ serviceName,
+ SERVICE_TYPE_LABELS,
+ List.of(ipV4Address) /* ipv4Address */,
+ List.of() /* ipv6Address */,
+ 5353 /* port */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
+ Collections.singletonMap("key", null) /* attributes */,
+ socketKey);
+
+ // Verify that onServiceFound is called.
+ inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
+ verifyServiceInfo(serviceInfoCaptor.getAllValues().get(1),
+ serviceName,
+ SERVICE_TYPE_LABELS,
+ List.of(ipV4Address) /* ipv4Address */,
+ List.of() /* ipv6Address */,
+ 5353 /* port */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
+ Collections.singletonMap("key", null) /* attributes */,
+ socketKey);
+
+ // Unregister the listener
+ stopSendAndReceive(mockListenerOne);
+ verify(mockDeps, times(2)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
+
+ // Register another listener.
+ startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+ verify(mockDeps, times(3)).removeMessages(any(), eq(EVENT_START_QUERYTASK));
+ InOrder inOrder2 = inOrder(mockListenerTwo);
+
+ // The services are cached in MdnsServiceCache, verify that onServiceNameDiscovered is
+ // called immediately.
+ inOrder2.verify(mockListenerTwo).onServiceNameDiscovered(serviceInfoCaptor.capture());
+ verifyServiceInfo(serviceInfoCaptor.getAllValues().get(2),
+ serviceName,
+ SERVICE_TYPE_LABELS,
+ List.of(ipV4Address) /* ipv4Address */,
+ List.of() /* ipv6Address */,
+ 5353 /* port */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
+ Collections.singletonMap("key", null) /* attributes */,
+ socketKey);
+
+ // The services are cached in MdnsServiceCache, verify that onServiceFound is
+ // called immediately.
+ inOrder2.verify(mockListenerTwo).onServiceFound(serviceInfoCaptor.capture());
+ verifyServiceInfo(serviceInfoCaptor.getAllValues().get(3),
+ serviceName,
+ SERVICE_TYPE_LABELS,
+ List.of(ipV4Address) /* ipv4Address */,
+ List.of() /* ipv6Address */,
+ 5353 /* port */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
+ Collections.singletonMap("key", null) /* attributes */,
+ socketKey);
+
+ // Process a response with a different ip address, port and updated text attributes.
+ final String ipV6Address = "2001:db8::";
+ processResponse(createResponse(
+ serviceName, ipV6Address, 5354, SUBTYPE,
+ Collections.singletonMap("key", "value"), TEST_TTL), socketKey);
+
+ // Verify the onServiceUpdated is called.
+ inOrder2.verify(mockListenerTwo).onServiceUpdated(serviceInfoCaptor.capture());
+ verifyServiceInfo(serviceInfoCaptor.getAllValues().get(4),
+ serviceName,
+ SERVICE_TYPE_LABELS,
+ List.of(ipV4Address) /* ipv4Address */,
+ List.of(ipV6Address) /* ipv6Address */,
+ 5354 /* port */,
+ Collections.singletonList(SUBTYPE) /* subTypes */,
+ Collections.singletonMap("key", "value") /* attributes */,
+ socketKey);
}
private static MdnsServiceInfo matchServiceName(String name) {