[automerger skipped] Merge "LinkProperties: use ordered comparison for P-CSCFs." into tm-mainline-prod am: 11817f2b62 -s ours am: fc38c393cf -s ours
am skip reason: Merged-In Ia1884f37739a20d871dd0024cc72cb18c083dc8e with SHA-1 78b8ff8493 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/23246157
Change-Id: I698f7b0d9203494f858df363435da52f28abe26e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
index 5d2f6e5..e17081a 100644
--- a/Cronet/tests/common/Android.bp
+++ b/Cronet/tests/common/Android.bp
@@ -26,7 +26,6 @@
// go with merging NetHttp and Tethering targets.
android_test {
name: "NetHttpCoverageTests",
- defaults: ["CronetTestJavaDefaults"],
enforce_default_target_sdk_version: true,
min_sdk_version: "30",
test_suites: ["general-tests", "mts-tethering"],
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
index 22eccf9..44b3364 100644
--- a/Cronet/tests/cts/Android.bp
+++ b/Cronet/tests/cts/Android.bp
@@ -18,38 +18,10 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// cronet_test_java_defaults can be used to specify a java_defaults target that
-// either enables or disables Cronet tests. This is used to disable Cronet
-// tests on tm-mainline-prod where the required APIs are not present.
-cronet_test_java_defaults = "CronetTestJavaDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_test_java_defaults may have different values
-// depending on the branch
-
-java_defaults {
- name: "CronetTestJavaDefaultsEnabled",
- enabled: true,
- // TODO(danstahr): move to unconditional static_libs once the T branch is abandoned
- static_libs: [
- "truth",
- ],
-}
-
-java_defaults {
- name: "CronetTestJavaDefaultsDisabled",
- enabled: false,
-}
-
-java_defaults {
- name: "CronetTestJavaDefaults",
- defaults: [cronet_test_java_defaults],
-}
-
android_library {
name: "CtsNetHttpTestsLib",
defaults: [
"cts_defaults",
- "CronetTestJavaDefaults",
],
sdk_version: "test_current",
min_sdk_version: "30",
@@ -61,10 +33,11 @@
"androidx.test.ext.junit",
"ctstestrunner-axt",
"ctstestserver",
- "junit",
"hamcrest-library",
+ "junit",
"kotlin-test",
"mockito-target",
+ "truth",
],
libs: [
"android.test.base",
@@ -79,7 +52,6 @@
name: "CtsNetHttpTestCases",
defaults: [
"cts_defaults",
- "CronetTestJavaDefaults",
],
sdk_version: "test_current",
static_libs: ["CtsNetHttpTestsLib"],
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index ecf4b7f..93564e4 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -19,7 +19,6 @@
java_genrule {
name: "net-http-test-jarjar-rules",
- defaults: ["CronetTestJavaDefaults"],
tool_files: [
":NetHttpTestsLibPreJarJar{.jar}",
"jarjar_excludes.txt",
@@ -37,7 +36,6 @@
android_library {
name: "NetHttpTestsLibPreJarJar",
- defaults: ["CronetTestJavaDefaults"],
srcs: [":cronet_aml_javatests_sources"],
sdk_version: "module_current",
min_sdk_version: "30",
@@ -46,6 +44,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"junit",
+ "truth",
],
libs: [
"android.test.base",
@@ -59,7 +58,6 @@
android_test {
name: "NetHttpTests",
defaults: [
- "CronetTestJavaDefaults",
"mts-target-sdk-version-current",
],
static_libs: ["NetHttpTestsLibPreJarJar"],
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 4506e5a..253fb00 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -50,22 +50,8 @@
// as the above target may have different "enabled" values
// depending on the branch
-// cronet_in_tethering_apex_defaults can be used to specify an apex_defaults target that either
-// enables or disables inclusion of Cronet in the Tethering apex. This is used to disable Cronet
-// on tm-mainline-prod. Note: in order for Cronet APIs to work Cronet must also be enabled
-// by the cronet_java_*_defaults in common/TetheringLib/Android.bp.
-cronet_in_tethering_apex_defaults = "CronetInTetheringApexDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_apex_defaults may have different values
-// depending on the branch
-
apex_defaults {
name: "CronetInTetheringApexDefaults",
- defaults: [cronet_in_tethering_apex_defaults],
-}
-
-apex_defaults {
- name: "CronetInTetheringApexDefaultsEnabled",
jni_libs: [
"cronet_aml_components_cronet_android_cronet",
"//external/cronet/third_party/boringssl:libcrypto",
@@ -83,10 +69,6 @@
},
}
-apex_defaults {
- name: "CronetInTetheringApexDefaultsDisabled",
-}
-
apex {
name: "com.android.tethering",
defaults: [
diff --git a/Tethering/apex/in-process b/Tethering/apex/in-process
deleted file mode 100644
index e69de29..0000000
--- a/Tethering/apex/in-process
+++ /dev/null
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 6b62da9..a4db776 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -17,16 +17,6 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-// Both cronet_java_defaults and cronet_java_prejarjar_defaults can be used to
-// specify a java_defaults target that either enables or disables Cronet. This
-// is used to disable Cronet on tm-mainline-prod.
-// Note: they must either both be enabled or disabled.
-cronet_java_defaults = "CronetJavaDefaultsEnabled"
-cronet_java_prejarjar_defaults = "CronetJavaPrejarjarDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_defaults may have different values
-// depending on the branch
-
java_sdk_library {
name: "framework-tethering",
defaults: [
@@ -67,44 +57,6 @@
lint: { strict_updatability_linting: true },
}
-java_defaults {
- name: "CronetJavaDefaults",
- defaults: [cronet_java_defaults],
-}
-
-java_defaults {
- name: "CronetJavaDefaultsEnabled",
- srcs: [":cronet_aml_api_sources"],
- libs: [
- "androidx.annotation_annotation",
- ],
- impl_only_static_libs: [
- "cronet_aml_java",
- ],
-}
-
-java_defaults {
- name: "CronetJavaDefaultsDisabled",
- api_dir: "cronet_disabled/api",
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaults",
- defaults: [cronet_java_prejarjar_defaults],
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaultsDisabled",
-}
-
-java_defaults {
- name: "CronetJavaPrejarjarDefaultsEnabled",
- static_libs: [
- "cronet_aml_api_java",
- "cronet_aml_java"
- ],
-}
-
java_library {
name: "framework-tethering-pre-jarjar",
defaults: [
diff --git a/Tethering/tests/integration/base/android/net/TetheringTester.java b/Tethering/tests/integration/base/android/net/TetheringTester.java
index ae39b24..1c0803e 100644
--- a/Tethering/tests/integration/base/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/base/android/net/TetheringTester.java
@@ -628,7 +628,7 @@
return false;
}
- private void sendUploadPacket(ByteBuffer packet) throws Exception {
+ public void sendUploadPacket(ByteBuffer packet) throws Exception {
mDownstreamReader.sendResponse(packet);
}
@@ -680,4 +680,12 @@
return verifyPacketNotNull("Download fail", getDownloadPacket(filter));
}
+
+ // Send DHCPDISCOVER to DHCP server to see if DHCP server is still alive to handle
+ // the upcoming DHCP packets. This method should be only used when we know the DHCP
+ // server has been created successfully before.
+ public boolean testDhcpServerAlive(final MacAddress mac) throws Exception {
+ sendDhcpDiscover(mac.toByteArray());
+ return getNextDhcpPacket() != null;
+ }
}
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 55854e2..21927df 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -39,6 +39,7 @@
import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -839,4 +840,41 @@
REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */,
tester, true /* isClat */);
}
+
+ private static final byte[] ZeroLengthDhcpPacket = new byte[] {
+ // scapy.Ether(
+ // dst="ff:ff:ff:ff:ff:ff")
+ // scapy.IP(
+ // dst="255.255.255.255")
+ // scapy.UDP(sport=68, dport=67)
+ /* Ethernet Header */
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xe0, (byte) 0x4f, (byte) 0x43, (byte) 0xe6, (byte) 0xfb, (byte) 0xd2,
+ (byte) 0x08, (byte) 0x00,
+ /* Ip header */
+ (byte) 0x45, (byte) 0x00, (byte) 0x00, (byte) 0x1c, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0xb6, (byte) 0x58,
+ (byte) 0x64, (byte) 0x4f, (byte) 0x60, (byte) 0x29, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff,
+ /* UDP header */
+ (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
+ (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf
+ };
+
+ @Test
+ public void testTetherZeroLengthDhcpPacket() throws Exception {
+ final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+ toList(TEST_IP4_DNS));
+ tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+ // Send a zero-length DHCP packet to upstream DHCP server.
+ final ByteBuffer packet = ByteBuffer.wrap(ZeroLengthDhcpPacket);
+ tester.sendUploadPacket(packet);
+
+ // Send DHCPDISCOVER packet from another downstream tethered device to verify that upstream
+ // DHCP server has closed the listening socket and stopped reading, then we will not receive
+ // any DHCPOFFER in this case.
+ final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66");
+ assertFalse(tester.testDhcpServerAlive(macAddress));
+ }
}
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
index b3fb3e4..81d4fbe 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -106,6 +106,7 @@
ConntrackMessage.Tuple tuple = ctmsg.tupleOrig;
if (nlmsghdr.nlmsg_type == (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW)
+ && tuple != null
&& tuple.protoNum == IPPROTO_TCP
&& tuple.srcIp.equals(local.getAddress())
&& tuple.dstIp.equals(remote.getAddress())
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index f2a3e62..3797a38 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -19,8 +19,8 @@
#include <netinet/in.h>
#include <stdint.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index f05b93e..85ba58e 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -30,8 +30,8 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
diff --git a/bpf_progs/dscpPolicy.c b/bpf_progs/dscpPolicy.c
index 72f63c6..262b65b 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -27,8 +27,8 @@
#include <stdint.h>
#include <string.h>
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include "bpf_helpers.h"
#include "dscpPolicy.h"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 39dff7f..839ca40 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-// The resulting .o needs to load on the Android T Beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
#include <bpf_helpers.h>
#include <linux/bpf.h>
diff --git a/framework/Android.bp b/framework/Android.bp
index d7eaf9b..123f02a 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -107,8 +107,11 @@
name: "framework-connectivity-pre-jarjar",
defaults: [
"framework-connectivity-defaults",
- "CronetJavaPrejarjarDefaults",
- ],
+ ],
+ static_libs: [
+ "cronet_aml_api_java",
+ "cronet_aml_java",
+ ],
libs: [
// This cannot be in the defaults clause above because if it were, it would be used
// to generate the connectivity stubs. That would create a circular dependency
@@ -120,6 +123,17 @@
visibility: ["//packages/modules/Connectivity:__subpackages__"]
}
+java_defaults {
+ name: "CronetJavaDefaults",
+ srcs: [":cronet_aml_api_sources"],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
+ impl_only_static_libs: [
+ "cronet_aml_java",
+ ],
+}
+
java_sdk_library {
name: "framework-connectivity",
defaults: [
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 3cc9c65..92e9599 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1348,6 +1348,18 @@
}
/**
+ * Gets the transports as an int. Internal callers only.
+ *
+ * Prefer getTransportTypes/hasTransportType if not immediately collapsing back into a scalar.
+ *
+ * @return a long integer representing the transport types.
+ * @hide
+ */
+ public long getTransportTypesInternal() {
+ return mTransportTypes;
+ }
+
+ /**
* Sets all the transports set on this {@code NetworkCapability} instance.
* This overwrites any existing transports.
*
diff --git a/netd/BpfBaseTest.cpp b/netd/BpfBaseTest.cpp
index 624d216..c979a7b 100644
--- a/netd/BpfBaseTest.cpp
+++ b/netd/BpfBaseTest.cpp
@@ -93,7 +93,7 @@
ASSERT_EQ(TEST_TAG, tagResult.value().tag);
ASSERT_EQ(0, close(sock));
// Check map periodically until sk destroy handler have done its job.
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 1000; i++) {
usleep(5000); // 5ms
tagResult = cookieTagMap.readValue(cookie);
if (!tagResult.ok()) {
@@ -101,7 +101,7 @@
return;
}
}
- FAIL() << "socket tag still exist after 50ms";
+ FAIL() << "socket tag still exist after 5s";
}
}
diff --git a/service-t/native/libs/libnetworkstats/Android.bp b/service-t/native/libs/libnetworkstats/Android.bp
index f40d388..0dfd0af 100644
--- a/service-t/native/libs/libnetworkstats/Android.bp
+++ b/service-t/native/libs/libnetworkstats/Android.bp
@@ -30,6 +30,7 @@
],
shared_libs: [
"libbase",
+ "libcutils",
"liblog",
],
static_libs: [
@@ -81,6 +82,7 @@
shared_libs: [
"libbase",
"liblog",
+ "libcutils",
"libandroid_net",
],
compile_multilib: "both",
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index 6aa0fb4..c5f9631 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -149,6 +149,18 @@
if (mIsTest) return; // Don't touch non-hermetic bpf in test.
if (mStarted) sPoller.Stop();
mStarted = false;
+
+ // Although this shouldn't be required, there seems to be some cases when we
+ // don't fill enough of a Perfetto Chunk for Perfetto to automatically commit
+ // the traced data. This manually flushes OnStop so we commit at least once.
+ NetworkTraceHandler::Trace([&](NetworkTraceHandler::TraceContext ctx) {
+ perfetto::LockedHandle<NetworkTraceHandler> handle =
+ ctx.GetDataSourceLocked();
+ // Trace is called for all active handlers, only flush our context. Since
+ // handle doesn't have a `.get()`, use `*` and `&` to get what it points to.
+ if (&(*handle) != this) return;
+ ctx.Flush();
+ });
}
void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
index 3de9897..d538368 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
@@ -15,10 +15,12 @@
*/
#define LOG_TAG "NetworkTrace"
+#define ATRACE_TAG ATRACE_TAG_NETWORK
#include "netdbpf/NetworkTracePoller.h"
#include <bpf/BpfUtils.h>
+#include <cutils/trace.h>
#include <log/log.h>
#include <perfetto/tracing/platform.h>
#include <perfetto/tracing/tracing.h>
@@ -133,6 +135,8 @@
return false;
}
+ ATRACE_INT("NetworkTracePackets", packets.size());
+
mCallback(packets);
return true;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 4af4c6a..29a03d1 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1394,7 +1394,8 @@
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
- mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"));
+ mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"),
+ handler.getLooper());
handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager));
mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider,
new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"));
@@ -1452,8 +1453,9 @@
*/
public MdnsDiscoveryManager makeMdnsDiscoveryManager(
@NonNull ExecutorProvider executorProvider,
- @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog) {
- return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog);
+ @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog,
+ @NonNull Looper looper) {
+ return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog, looper);
}
/**
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
index cc08ea1..5f27b6a 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java
@@ -253,8 +253,9 @@
int getConflictingService(@NonNull NsdServiceInfo info) {
for (int i = 0; i < mPendingRegistrations.size(); i++) {
final NsdServiceInfo other = mPendingRegistrations.valueAt(i).getServiceInfo();
- if (info.getServiceName().equals(other.getServiceName())
- && info.getServiceType().equals(other.getServiceType())) {
+ if (MdnsUtils.equalsIgnoreDnsCase(info.getServiceName(), other.getServiceName())
+ && MdnsUtils.equalsIgnoreDnsCase(info.getServiceType(),
+ other.getServiceType())) {
return mPendingRegistrations.keyAt(i);
}
}
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 6455044..0154827 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -16,20 +16,24 @@
package com.android.server.connectivity.mdns;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
import static com.android.server.connectivity.mdns.util.MdnsUtils.isNetworkMatched;
+import static com.android.server.connectivity.mdns.util.MdnsUtils.isRunningOnHandlerThread;
import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.SharedLog;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -47,8 +51,8 @@
private final MdnsSocketClientBase socketClient;
@NonNull private final SharedLog sharedLog;
- @GuardedBy("this")
@NonNull private final PerNetworkServiceTypeClients perNetworkServiceTypeClients;
+ @NonNull private final Handler handler;
private static class PerNetworkServiceTypeClients {
private final ArrayMap<Pair<String, Network>, MdnsServiceTypeClient> clients =
@@ -56,21 +60,26 @@
public void put(@NonNull String serviceType, @Nullable Network network,
@NonNull MdnsServiceTypeClient client) {
- final Pair<String, Network> perNetworkServiceType = new Pair<>(serviceType, network);
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
+ final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType,
+ network);
clients.put(perNetworkServiceType, client);
}
@Nullable
public MdnsServiceTypeClient get(@NonNull String serviceType, @Nullable Network network) {
- final Pair<String, Network> perNetworkServiceType = new Pair<>(serviceType, network);
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
+ final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType,
+ network);
return clients.getOrDefault(perNetworkServiceType, null);
}
public List<MdnsServiceTypeClient> getByServiceType(@NonNull String serviceType) {
+ final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) {
final Pair<String, Network> perNetworkServiceType = clients.keyAt(i);
- if (serviceType.equals(perNetworkServiceType.first)) {
+ if (dnsLowerServiceType.equals(perNetworkServiceType.first)) {
list.add(clients.valueAt(i));
}
}
@@ -103,11 +112,21 @@
}
public MdnsDiscoveryManager(@NonNull ExecutorProvider executorProvider,
- @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog) {
+ @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog,
+ @NonNull Looper looper) {
this.executorProvider = executorProvider;
this.socketClient = socketClient;
this.sharedLog = sharedLog;
perNetworkServiceTypeClients = new PerNetworkServiceTypeClients();
+ handler = new Handler(looper);
+ }
+
+ private void checkAndRunOnHandlerThread(@NonNull Runnable function) {
+ if (isRunningOnHandlerThread(handler)) {
+ function.run();
+ } else {
+ handler.post(function);
+ }
}
/**
@@ -120,11 +139,19 @@
* serviceType}.
*/
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
- public synchronized void registerListener(
+ public void registerListener(
@NonNull String serviceType,
@NonNull MdnsServiceBrowserListener listener,
@NonNull MdnsSearchOptions searchOptions) {
sharedLog.i("Registering listener for serviceType: " + serviceType);
+ checkAndRunOnHandlerThread(() ->
+ handleRegisterListener(serviceType, listener, searchOptions));
+ }
+
+ private void handleRegisterListener(
+ @NonNull String serviceType,
+ @NonNull MdnsServiceBrowserListener listener,
+ @NonNull MdnsSearchOptions searchOptions) {
if (perNetworkServiceTypeClients.isEmpty()) {
// First listener. Starts the socket client.
try {
@@ -139,30 +166,28 @@
new MdnsSocketClientBase.SocketCreationCallback() {
@Override
public void onSocketCreated(@Nullable Network network) {
- synchronized (MdnsDiscoveryManager.this) {
- // All listeners of the same service types shares the same
- // MdnsServiceTypeClient.
- MdnsServiceTypeClient serviceTypeClient =
- perNetworkServiceTypeClients.get(serviceType, network);
- if (serviceTypeClient == null) {
- serviceTypeClient = createServiceTypeClient(serviceType, network);
- perNetworkServiceTypeClients.put(serviceType, network,
- serviceTypeClient);
- }
- serviceTypeClient.startSendAndReceive(listener, searchOptions);
+ ensureRunningOnHandlerThread(handler);
+ // All listeners of the same service types shares the same
+ // MdnsServiceTypeClient.
+ MdnsServiceTypeClient serviceTypeClient =
+ perNetworkServiceTypeClients.get(serviceType, network);
+ if (serviceTypeClient == null) {
+ serviceTypeClient = createServiceTypeClient(serviceType, network);
+ perNetworkServiceTypeClients.put(serviceType, network,
+ serviceTypeClient);
}
+ serviceTypeClient.startSendAndReceive(listener, searchOptions);
}
@Override
- public void onSocketDestroyed(@Nullable Network network) {
- synchronized (MdnsDiscoveryManager.this) {
- final MdnsServiceTypeClient serviceTypeClient =
- perNetworkServiceTypeClients.get(serviceType, network);
- if (serviceTypeClient == null) return;
- // Notify all listeners that all services are removed from this socket.
- serviceTypeClient.notifyAllServicesRemoved();
- perNetworkServiceTypeClients.remove(serviceTypeClient);
- }
+ public void onAllSocketsDestroyed(@Nullable Network network) {
+ ensureRunningOnHandlerThread(handler);
+ final MdnsServiceTypeClient serviceTypeClient =
+ perNetworkServiceTypeClients.get(serviceType, network);
+ if (serviceTypeClient == null) return;
+ // Notify all listeners that all services are removed from this socket.
+ serviceTypeClient.notifySocketDestroyed();
+ perNetworkServiceTypeClients.remove(serviceTypeClient);
}
});
}
@@ -175,9 +200,14 @@
* @param listener The {@link MdnsServiceBrowserListener} listener.
*/
@RequiresPermission(permission.CHANGE_WIFI_MULTICAST_STATE)
- public synchronized void unregisterListener(
+ public void unregisterListener(
@NonNull String serviceType, @NonNull MdnsServiceBrowserListener listener) {
sharedLog.i("Unregistering listener for serviceType:" + serviceType);
+ checkAndRunOnHandlerThread(() -> handleUnregisterListener(serviceType, listener));
+ }
+
+ private void handleUnregisterListener(
+ @NonNull String serviceType, @NonNull MdnsServiceBrowserListener listener) {
final List<MdnsServiceTypeClient> serviceTypeClients =
perNetworkServiceTypeClients.getByServiceType(serviceType);
if (serviceTypeClients.isEmpty()) {
@@ -200,8 +230,14 @@
}
@Override
- public synchronized void onResponseReceived(@NonNull MdnsPacket packet,
- int interfaceIndex, Network network) {
+ public void onResponseReceived(@NonNull MdnsPacket packet,
+ int interfaceIndex, @Nullable Network network) {
+ checkAndRunOnHandlerThread(() ->
+ handleOnResponseReceived(packet, interfaceIndex, network));
+ }
+
+ private void handleOnResponseReceived(@NonNull MdnsPacket packet, int interfaceIndex,
+ @Nullable Network network) {
for (MdnsServiceTypeClient serviceTypeClient
: perNetworkServiceTypeClients.getByMatchingNetwork(network)) {
serviceTypeClient.processResponse(packet, interfaceIndex, network);
@@ -209,8 +245,14 @@
}
@Override
- public synchronized void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
- Network network) {
+ public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
+ @Nullable Network network) {
+ checkAndRunOnHandlerThread(() ->
+ handleOnFailedToParseMdnsResponse(receivedPacketNumber, errorCode, network));
+ }
+
+ private void handleOnFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
+ @Nullable Network network) {
for (MdnsServiceTypeClient serviceTypeClient
: perNetworkServiceTypeClients.getByMatchingNetwork(network)) {
serviceTypeClient.onFailedToParseMdnsResponse(receivedPacketNumber, errorCode);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index 6414453..52c8622 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -34,7 +34,6 @@
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.List;
-import java.util.Map;
/**
* The {@link MdnsMultinetworkSocketClient} manages the multinetwork socket for mDns
@@ -48,9 +47,8 @@
@NonNull private final Handler mHandler;
@NonNull private final MdnsSocketProvider mSocketProvider;
- private final Map<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
+ private final ArrayMap<MdnsServiceBrowserListener, InterfaceSocketCallback> mRequestedNetworks =
new ArrayMap<>();
- private final ArrayMap<MdnsInterfaceSocket, Network> mActiveNetworkSockets = new ArrayMap<>();
private final ArrayMap<MdnsInterfaceSocket, ReadPacketHandler> mSocketPacketHandlers =
new ArrayMap<>();
private MdnsSocketClientBase.Callback mCallback = null;
@@ -63,7 +61,11 @@
}
private class InterfaceSocketCallback implements MdnsSocketProvider.SocketCallback {
+ @NonNull
private final SocketCreationCallback mSocketCreationCallback;
+ @NonNull
+ private final ArrayMap<MdnsInterfaceSocket, Network> mActiveNetworkSockets =
+ new ArrayMap<>();
InterfaceSocketCallback(SocketCreationCallback socketCreationCallback) {
mSocketCreationCallback = socketCreationCallback;
@@ -88,10 +90,59 @@
@Override
public void onInterfaceDestroyed(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket) {
- mSocketPacketHandlers.remove(socket);
- mActiveNetworkSockets.remove(socket);
- mSocketCreationCallback.onSocketDestroyed(network);
+ notifySocketDestroyed(socket);
+ maybeCleanupPacketHandler(socket);
}
+
+ private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
+ final Network network = mActiveNetworkSockets.remove(socket);
+ if (!isAnySocketActive(network)) {
+ mSocketCreationCallback.onAllSocketsDestroyed(network);
+ }
+ }
+
+ void onNetworkUnrequested() {
+ for (int i = mActiveNetworkSockets.size() - 1; i >= 0; i--) {
+ // Iterate from the end so the socket can be removed
+ final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
+ notifySocketDestroyed(socket);
+ maybeCleanupPacketHandler(socket);
+ }
+ }
+ }
+
+ private boolean isSocketActive(@NonNull MdnsInterfaceSocket socket) {
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ if (isc.mActiveNetworkSockets.containsKey(socket)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isAnySocketActive(@Nullable Network network) {
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ if (isc.mActiveNetworkSockets.containsValue(network)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ArrayMap<MdnsInterfaceSocket, Network> getActiveSockets() {
+ final ArrayMap<MdnsInterfaceSocket, Network> sockets = new ArrayMap<>();
+ for (int i = 0; i < mRequestedNetworks.size(); i++) {
+ final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
+ sockets.putAll(isc.mActiveNetworkSockets);
+ }
+ return sockets;
+ }
+
+ private void maybeCleanupPacketHandler(@NonNull MdnsInterfaceSocket socket) {
+ if (isSocketActive(socket)) return;
+ mSocketPacketHandlers.remove(socket);
}
private class ReadPacketHandler implements MulticastPacketReader.PacketHandler {
@@ -143,11 +194,14 @@
@Override
public void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) {
ensureRunningOnHandlerThread(mHandler);
- final InterfaceSocketCallback callback = mRequestedNetworks.remove(listener);
+ final InterfaceSocketCallback callback = mRequestedNetworks.get(listener);
if (callback == null) {
Log.e(TAG, "Can not be unrequested with unknown listener=" + listener);
return;
}
+ callback.onNetworkUnrequested();
+ // onNetworkUnrequested does cleanups based on mRequestedNetworks, only remove afterwards
+ mRequestedNetworks.remove(listener);
mSocketProvider.unrequestSocket(callback);
}
@@ -156,9 +210,10 @@
instanceof Inet6Address;
final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet4Address;
- for (int i = 0; i < mActiveNetworkSockets.size(); i++) {
- final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
- final Network network = mActiveNetworkSockets.valueAt(i);
+ final ArrayMap<MdnsInterfaceSocket, Network> activeSockets = getActiveSockets();
+ for (int i = 0; i < activeSockets.size(); i++) {
+ final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
+ final Network network = activeSockets.valueAt(i);
// Check ip capability and network before sending packet
if (((isIpv6 && socket.hasJoinedIpv6()) || (isIpv4 && socket.hasJoinedIpv4()))
&& isNetworkMatched(targetNetwork, network)) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
index 6ec2f99..1239180 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsNsecRecord.java
@@ -20,6 +20,7 @@
import android.text.TextUtils;
import com.android.net.module.util.CollectionUtils;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -152,7 +153,8 @@
@Override
public int hashCode() {
return Objects.hash(super.hashCode(),
- Arrays.hashCode(mNextDomain), Arrays.hashCode(mTypes));
+ Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(mNextDomain)),
+ Arrays.hashCode(mTypes));
}
@Override
@@ -165,7 +167,8 @@
}
return super.equals(other)
- && Arrays.equals(mNextDomain, ((MdnsNsecRecord) other).mNextDomain)
+ && MdnsUtils.equalsDnsLabelIgnoreDnsCase(mNextDomain,
+ ((MdnsNsecRecord) other).mNextDomain)
&& Arrays.equals(mTypes, ((MdnsNsecRecord) other).mTypes);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
index c0f9b8b..6879a64 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPacketWriter.java
@@ -17,11 +17,11 @@
package com.android.server.connectivity.mdns;
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.SocketAddress;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -180,7 +180,7 @@
int existingOffset = entry.getKey();
String[] existingLabels = entry.getValue();
- if (Arrays.equals(existingLabels, labels)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(existingLabels, labels)) {
writePointer(existingOffset);
return;
} else if (MdnsRecord.labelsAreSuffix(existingLabels, labels)) {
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
index 2c7b26b..c88ead0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsPointerRecord.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -60,7 +61,8 @@
}
public boolean hasSubtype() {
- return (name != null) && (name.length > 2) && name[1].equals(MdnsConstants.SUBTYPE_LABEL);
+ return (name != null) && (name.length > 2) && MdnsUtils.equalsIgnoreDnsCase(name[1],
+ MdnsConstants.SUBTYPE_LABEL);
}
public String getSubtype() {
@@ -74,7 +76,7 @@
@Override
public int hashCode() {
- return (super.hashCode() * 31) + Arrays.hashCode(pointer);
+ return (super.hashCode() * 31) + Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(pointer));
}
@Override
@@ -86,6 +88,7 @@
return false;
}
- return super.equals(other) && Arrays.equals(pointer, ((MdnsPointerRecord) other).pointer);
+ return super.equals(other) && MdnsUtils.equalsDnsLabelIgnoreDnsCase(pointer,
+ ((MdnsPointerRecord) other).pointer);
}
}
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
index 669b323..ecf846e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsProber.java
@@ -21,9 +21,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -113,7 +113,8 @@
*/
private static boolean containsName(@NonNull List<MdnsRecord> records,
@NonNull String[] name) {
- return CollectionUtils.any(records, r -> Arrays.equals(name, r.getName()));
+ return CollectionUtils.any(records,
+ r -> MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, r.getName()));
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
index 19630e3..28bd1b4 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecord.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -136,7 +137,7 @@
}
for (int i = 0; i < list1.length; ++i) {
- if (!list1[i].equals(list2[i + offset])) {
+ if (!MdnsUtils.equalsIgnoreDnsCase(list1[i], list2[i + offset])) {
return false;
}
}
@@ -271,12 +272,13 @@
MdnsRecord otherRecord = (MdnsRecord) other;
- return Arrays.equals(name, otherRecord.name) && (type == otherRecord.type);
+ return MdnsUtils.equalsDnsLabelIgnoreDnsCase(name, otherRecord.name) && (type
+ == otherRecord.type);
}
@Override
public int hashCode() {
- return Objects.hash(Arrays.hashCode(name), type);
+ return Objects.hash(Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(name)), type);
}
/**
@@ -297,7 +299,7 @@
public Key(int recordType, String[] recordName) {
this.recordType = recordType;
- this.recordName = recordName;
+ this.recordName = MdnsUtils.toDnsLabelsLowerCase(recordName);
}
@Override
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
index f756459..1375279 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsRecordRepository.java
@@ -30,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.HexDump;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.net.Inet4Address;
@@ -329,7 +330,8 @@
private int getServiceByName(@NonNull String serviceName) {
for (int i = 0; i < mServices.size(); i++) {
final ServiceRegistration registration = mServices.valueAt(i);
- if (serviceName.equals(registration.serviceInfo.getServiceName())) {
+ if (MdnsUtils.equalsIgnoreDnsCase(serviceName,
+ registration.serviceInfo.getServiceName())) {
return mServices.keyAt(i);
}
}
@@ -545,7 +547,9 @@
must match the question qtype unless the qtype is "ANY" (255) or the rrtype is
"CNAME" (5), and the record rrclass must match the question qclass unless the
qclass is "ANY" (255) */
- if (!Arrays.equals(info.record.getName(), question.getName())) continue;
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(info.record.getName(), question.getName())) {
+ continue;
+ }
hasFullyOwnedNameMatch |= !info.isSharedName;
// The repository does not store CNAME records
@@ -747,7 +751,10 @@
// happens. In fact probing is only done for the service name in the SRV record.
// This means only SRV and TXT records need to be checked.
final RecordInfo<MdnsServiceRecord> srvRecord = registration.srvRecord;
- if (!Arrays.equals(record.getName(), srvRecord.record.getName())) continue;
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(record.getName(),
+ srvRecord.record.getName())) {
+ continue;
+ }
// As per RFC6762 9., it's fine if the "conflict" is an identical record with same
// data.
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
index e0100f6..28aa640 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -21,10 +21,10 @@
import android.net.Network;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@@ -103,7 +103,7 @@
* pointer record is already present in the response with the same TTL.
*/
public synchronized boolean addPointerRecord(MdnsPointerRecord pointerRecord) {
- if (!Arrays.equals(serviceName, pointerRecord.getPointer())) {
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceName, pointerRecord.getPointer())) {
throw new IllegalArgumentException(
"Pointer records for different service names cannot be added");
}
@@ -301,13 +301,13 @@
boolean dropAddressRecords = false;
for (MdnsInetAddressRecord inetAddressRecord : getInet4AddressRecords()) {
- if (!Arrays.equals(
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
}
for (MdnsInetAddressRecord inetAddressRecord : getInet6AddressRecords()) {
- if (!Arrays.equals(
+ if (!MdnsUtils.equalsDnsLabelIgnoreDnsCase(
this.serviceRecord.getServiceHost(), inetAddressRecord.getName())) {
dropAddressRecords = true;
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
index 129db7e..77b5c58 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponseDecoder.java
@@ -23,10 +23,10 @@
import android.util.ArraySet;
import com.android.server.connectivity.mdns.util.MdnsLogger;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.EOFException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -50,7 +50,7 @@
List<MdnsResponse> responses, String[] pointer) {
if (responses != null) {
for (MdnsResponse response : responses) {
- if (Arrays.equals(response.getServiceName(), pointer)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(response.getServiceName(), pointer)) {
return response;
}
}
@@ -66,7 +66,8 @@
if (serviceRecord == null) {
continue;
}
- if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(),
+ hostName)) {
return response;
}
}
@@ -164,11 +165,8 @@
for (MdnsRecord record : records) {
if (record instanceof MdnsPointerRecord) {
String[] name = record.getName();
- if ((serviceType == null)
- || Arrays.equals(name, serviceType)
- || ((name.length == (serviceType.length + 2))
- && name[1].equals(MdnsConstants.SUBTYPE_LABEL)
- && MdnsRecord.labelsAreSuffix(serviceType, name))) {
+ if ((serviceType == null) || MdnsUtils.typeEqualsOrIsSubtype(
+ serviceType, name)) {
MdnsPointerRecord pointerRecord = (MdnsPointerRecord) record;
// Group PTR records that refer to the same service instance name into a single
// response.
@@ -296,7 +294,7 @@
if (serviceRecord == null) {
continue;
}
- if (Arrays.equals(serviceRecord.getServiceHost(), hostName)) {
+ if (MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceRecord.getServiceHost(), hostName)) {
if (result == null) {
result = new ArrayList<>(/* initialCapacity= */ responses.size());
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
index ebd8b77..f851b35 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceRecord.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException;
import java.util.Arrays;
@@ -142,7 +143,8 @@
@Override
public int hashCode() {
return (super.hashCode() * 31)
- + Objects.hash(servicePriority, serviceWeight, Arrays.hashCode(serviceHost),
+ + Objects.hash(servicePriority, serviceWeight,
+ Arrays.hashCode(MdnsUtils.toDnsLabelsLowerCase(serviceHost)),
servicePort);
}
@@ -159,7 +161,7 @@
return super.equals(other)
&& (servicePriority == otherRecord.servicePriority)
&& (serviceWeight == otherRecord.serviceWeight)
- && Arrays.equals(serviceHost, otherRecord.serviceHost)
+ && MdnsUtils.equalsDnsLabelIgnoreDnsCase(serviceHost, otherRecord.serviceHost)
&& (servicePort == otherRecord.servicePort);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index 4e6571f..e56bd5b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -284,7 +284,7 @@
}
/** Notify all services are removed because the socket is destroyed. */
- public void notifyAllServicesRemoved() {
+ public void notifySocketDestroyed() {
synchronized (lock) {
for (MdnsResponse response : instanceNameToResponse.values()) {
final String name = response.getServiceInstanceName();
@@ -302,6 +302,11 @@
listener.onServiceNameRemoved(serviceInfo);
}
}
+
+ if (requestTaskFuture != null) {
+ requestTaskFuture.cancel(true);
+ requestTaskFuture = null;
+ }
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
index 6bcad01..c614cd3 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
@@ -88,6 +88,6 @@
void onSocketCreated(@Nullable Network network);
/*** Notify requested socket is destroyed */
- void onSocketDestroyed(@Nullable Network network);
+ void onAllSocketsDestroyed(@Nullable Network network);
}
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index ca61d34..2fa1ae4 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -414,8 +414,10 @@
if (networkKey == LOCAL_NET) {
transports = new int[0];
} else {
- transports = mActiveNetworksTransports.getOrDefault(
- ((NetworkAsKey) networkKey).mNetwork, new int[0]);
+ transports = mActiveNetworksTransports.get(((NetworkAsKey) networkKey).mNetwork);
+ if (transports == null) {
+ Log.wtf(TAG, "transports is missing for key: " + networkKey);
+ }
}
if (networkInterface == null || !isMdnsCapableInterface(networkInterface, transports)) {
return;
@@ -599,8 +601,6 @@
if (matchRequestedNetwork(network)) continue;
final SocketInfo info = mNetworkSockets.removeAt(i);
info.mSocket.destroy();
- // Still notify to unrequester for socket destroy.
- cb.onInterfaceDestroyed(network, info.mSocket);
mSharedLog.log("Remove socket on net:" + network + " after unrequestSocket");
}
@@ -610,8 +610,6 @@
for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
info.mSocket.destroy();
- // Still notify to unrequester for socket destroy.
- cb.onInterfaceDestroyed(null /* network */, info.mSocket);
mSharedLog.log("Remove socket on ifName:" + mTetherInterfaceSockets.keyAt(i)
+ " after unrequestSocket");
}
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 5cc789f..bc94869 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
@@ -21,6 +21,9 @@
import android.net.Network;
import android.os.Handler;
+import com.android.server.connectivity.mdns.MdnsConstants;
+import com.android.server.connectivity.mdns.MdnsRecord;
+
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
@@ -50,6 +53,17 @@
}
/**
+ * Convert the array of labels to DNS case-insensitive lowercase.
+ */
+ public static String[] toDnsLabelsLowerCase(@NonNull String[] labels) {
+ final String[] outStrings = new String[labels.length];
+ for (int i = 0; i < labels.length; ++i) {
+ outStrings[i] = toDnsLowerCase(labels[i]);
+ }
+ return outStrings;
+ }
+
+ /**
* Compare two strings by DNS case-insensitive lowercase.
*/
public static boolean equalsIgnoreDnsCase(@NonNull String a, @NonNull String b) {
@@ -62,18 +76,59 @@
return true;
}
+ /**
+ * Compare two set of DNS labels by DNS case-insensitive lowercase.
+ */
+ public static boolean equalsDnsLabelIgnoreDnsCase(@NonNull String[] a, @NonNull String[] b) {
+ if (a == b) {
+ return true;
+ }
+ int length = a.length;
+ if (b.length != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!equalsIgnoreDnsCase(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare labels a equals b or a is suffix of b.
+ *
+ * @param a the type or subtype.
+ * @param b the base type
+ */
+ public static boolean typeEqualsOrIsSubtype(@NonNull String[] a,
+ @NonNull String[] b) {
+ return MdnsUtils.equalsDnsLabelIgnoreDnsCase(a, b)
+ || ((b.length == (a.length + 2))
+ && MdnsUtils.equalsIgnoreDnsCase(b[1], MdnsConstants.SUBTYPE_LABEL)
+ && MdnsRecord.labelsAreSuffix(a, b));
+ }
+
private static char toDnsLowerCase(char a) {
return a >= 'A' && a <= 'Z' ? (char) (a + ('a' - 'A')) : a;
}
/*** Ensure that current running thread is same as given handler thread */
public static void ensureRunningOnHandlerThread(@NonNull Handler handler) {
- if (handler.getLooper().getThread() != Thread.currentThread()) {
+ if (!isRunningOnHandlerThread(handler)) {
throw new IllegalStateException(
"Not running on Handler thread: " + Thread.currentThread().getName());
}
}
+ /*** Check that current running thread is same as given handler thread */
+ public static boolean isRunningOnHandlerThread(@NonNull Handler handler) {
+ if (handler.getLooper().getThread() == Thread.currentThread()) {
+ return true;
+ }
+ return false;
+ }
+
/*** Check whether the target network is matched current network */
public static boolean isNetworkMatched(@Nullable Network targetNetwork,
@Nullable Network currentNetwork) {
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index f977a27..e7ef510 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -46,6 +46,7 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_TEST;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -1582,7 +1583,9 @@
// For a template with wifi network keys, it is possible for a malicious
// client to track the user locations via querying data usage. Thus, enforce
// fine location permission check.
- if (!template.getWifiNetworkKeys().isEmpty()) {
+ // For a template with MATCH_TEST, since the wifi network key is just a placeholder
+ // to identify a specific test network, it is not related to track user location.
+ if (!template.getWifiNetworkKeys().isEmpty() && template.getMatchRule() != MATCH_TEST) {
final boolean canAccessFineLocation = mLocationPermissionChecker
.checkCallersLocationPermission(callingPackage,
null /* featureId */,
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 77cffda..9ced44e 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -54,6 +54,10 @@
if (!isOk(status)) {
uid_t uid = getuid();
ALOGE("BpfNetMaps jni init failure as uid=%d", uid);
+ // We probably only ever get called from system_server (ie. AID_SYSTEM)
+ // or from tests, and never from network_stack (ie. AID_NETWORK_STACK).
+ // However, if we ever do add calls from production network_stack code
+ // we do want to make sure this initializes correctly.
// TODO: Fix tests to not use this jni lib, so we can unconditionally abort()
if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort();
}
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 059b716..d966070 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -90,11 +90,6 @@
#undef ALOGF
-bool isGsiImage() {
- // this implementation matches 2 other places in the codebase (same function name too)
- return !access("/system/system_ext/etc/init/init.gsi.rc", F_OK);
-}
-
static const char* kClatdDir = "/apex/com.android.tethering/bin/for-system";
static const char* kClatdBin = "/apex/com.android.tethering/bin/for-system/clatd";
@@ -135,14 +130,6 @@
#undef V2
- // HACK: Some old vendor kernels lack ~5.10 backport of 'bpffs selinux genfscon' support.
- // This is *NOT* supported, but let's allow, at least for now, U+ GSI to boot on them.
- // (without this hack pixel5 R vendor + U gsi breaks)
- if (isGsiImage() && !bpf::isAtLeastKernelVersion(5, 10, 0)) {
- ALOGE("GSI with *BAD* pre-5.10 kernel lacking bpffs selinux genfscon support.");
- return;
- }
-
if (fatal) abort();
}
@@ -485,11 +472,15 @@
static constexpr int WAITPID_ATTEMPTS = 50;
static constexpr int WAITPID_RETRY_INTERVAL_US = 100000;
-static void stopClatdProcess(int pid) {
- int err = kill(pid, SIGTERM);
- if (err) {
- err = errno;
+static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
+ jint pid) {
+ if (pid <= 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
+ return;
}
+
+ int err = kill(pid, SIGTERM);
+ if (err) err = errno;
if (err == ESRCH) {
ALOGE("clatd child process %d unexpectedly disappeared", pid);
return;
@@ -518,23 +509,6 @@
}
}
-static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
- jstring iface, jstring pfx96,
- jstring v4, jstring v6,
- jint pid) {
- ScopedUtfChars ifaceStr(env, iface);
- ScopedUtfChars pfx96Str(env, pfx96);
- ScopedUtfChars v4Str(env, v4);
- ScopedUtfChars v6Str(env, v6);
-
- if (pid <= 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
- return;
- }
-
- stopClatdProcess(pid);
-}
-
static jlong com_android_server_connectivity_ClatCoordinator_getSocketCookie(
JNIEnv* env, jclass clazz, jobject sockJavaFd) {
int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
@@ -579,8 +553,7 @@
"(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)com_android_server_connectivity_ClatCoordinator_startClatd},
- {"native_stopClatd",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
+ {"native_stopClatd", "(I)V",
(void*)com_android_server_connectivity_ClatCoordinator_stopClatd},
{"native_getSocketCookie", "(Ljava/io/FileDescriptor;)J",
(void*)com_android_server_connectivity_ClatCoordinator_getSocketCookie},
diff --git a/service/jni/onload.cpp b/service/jni/onload.cpp
index 3d15d43..ed74430 100644
--- a/service/jni/onload.cpp
+++ b/service/jni/onload.cpp
@@ -22,8 +22,8 @@
namespace android {
int register_com_android_server_TestNetworkService(JNIEnv* env);
-int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
int register_com_android_server_BpfNetMaps(JNIEnv* env);
+int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
@@ -38,15 +38,15 @@
return JNI_ERR;
}
- if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
- return JNI_ERR;
- }
-
- if (register_com_android_server_BpfNetMaps(env) < 0) {
- return JNI_ERR;
- }
-
if (android::modules::sdklevel::IsAtLeastT()) {
+ if (register_com_android_server_BpfNetMaps(env) < 0) {
+ return JNI_ERR;
+ }
+
+ if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
+ return JNI_ERR;
+ }
+
if (register_android_server_net_NetworkStatsFactory(env) < 0) {
return JNI_ERR;
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8ec6eb9..b17af99 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2663,7 +2663,8 @@
final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null && nai.everConnected()) {
+ final boolean includeNetwork = (nai != null) && nai.isCreated();
+ if (includeNetwork) {
// TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -3889,9 +3890,9 @@
break;
}
case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: {
- if (!nai.isCreated()) {
- Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString()
- + ", tearing down instead");
+ if (!nai.everConnected()) {
+ Log.d(TAG, "unregisterAfterReplacement on never-connected "
+ + nai.toShortString() + ", tearing down instead");
teardownUnneededNetwork(nai);
break;
}
@@ -4476,6 +4477,25 @@
}
}
+ @VisibleForTesting
+ protected static boolean shouldCreateNetworksImmediately() {
+ // Before U, physical networks are only created when the agent advances to CONNECTED.
+ // In U and above, all networks are immediately created when the agent is registered.
+ return SdkLevel.isAtLeastU();
+ }
+
+ private static boolean shouldCreateNativeNetwork(@NonNull NetworkAgentInfo nai,
+ @NonNull NetworkInfo.State state) {
+ if (nai.isCreated()) return false;
+ if (state == NetworkInfo.State.CONNECTED) return true;
+ if (state != NetworkInfo.State.CONNECTING) {
+ // TODO: throw if no WTFs are observed in the field.
+ Log.wtf(TAG, "Uncreated network in invalid state: " + state);
+ return false;
+ }
+ return nai.isVPN() || shouldCreateNetworksImmediately();
+ }
+
private static boolean shouldDestroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
return nai.isCreated() && !nai.isDestroyed();
}
@@ -7918,7 +7938,7 @@
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
- } else {
+ } else if (networkAgent.everConnected()) {
updateProxy(newLp, oldLp);
}
@@ -7952,6 +7972,10 @@
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
+ private void applyInitialLinkProperties(@NonNull NetworkAgentInfo nai) {
+ updateLinkProperties(nai, new LinkProperties(nai.linkProperties), null);
+ }
+
/**
* @param naData captive portal data from NetworkAgent
* @param apiData captive portal data from capport API
@@ -9714,21 +9738,32 @@
+ oldInfo.getState() + " to " + state);
}
- if (!networkAgent.isCreated()
- && (state == NetworkInfo.State.CONNECTED
- || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
-
+ if (shouldCreateNativeNetwork(networkAgent, state)) {
// A network that has just connected has zero requests and is thus a foreground network.
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
+
+ networkAgent.setCreated();
+
+ // If the network is created immediately on register, then apply the LinkProperties now.
+ // Otherwise, this is done further down when the network goes into connected state.
+ // Applying the LinkProperties means that the network is ready to carry traffic -
+ // interfaces and routing rules have been added, DNS servers programmed, etc.
+ // For VPNs, this must be done before the capabilities are updated, because as soon as
+ // that happens, UIDs are routed to the network.
+ if (shouldCreateNetworksImmediately()) {
+ applyInitialLinkProperties(networkAgent);
+ }
+
+ // TODO: should this move earlier? It doesn't seem to have anything to do with whether
+ // a network is created or not.
if (networkAgent.propagateUnderlyingCapabilities()) {
// Initialize the network's capabilities to their starting values according to the
// underlying networks. This ensures that the capabilities are correct before
// anything happens to the network.
updateCapabilitiesForNetwork(networkAgent);
}
- networkAgent.setCreated();
networkAgent.onNetworkCreated();
updateAllowedUids(networkAgent, null, networkAgent.networkCapabilities);
updateProfileAllowedNetworks();
@@ -9742,8 +9777,19 @@
networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
- updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
- null);
+ if (!shouldCreateNetworksImmediately()) {
+ applyInitialLinkProperties(networkAgent);
+ } else {
+ // The network was created when the agent registered, and the LinkProperties are
+ // already up-to-date. However, updateLinkProperties also makes some changes only
+ // when the network connects. Apply those changes here. On T and below these are
+ // handled by the applyInitialLinkProperties call just above.
+ // TODO: stop relying on updateLinkProperties(..., null) to do this.
+ // If something depends on both LinkProperties and connected state, it should be in
+ // this method as well.
+ networkAgent.clatd.update();
+ updateProxy(networkAgent.linkProperties, null);
+ }
// If a rate limit has been configured and is applicable to this network (network
// provides internet connectivity), apply it. The tc police filter cannot be attached
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 5d04632..fbe706c 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -237,9 +237,8 @@
/**
* Stop clatd.
*/
- public void stopClatd(String iface, String pfx96, String v4, String v6, int pid)
- throws IOException {
- native_stopClatd(iface, pfx96, v4, v6, pid);
+ public void stopClatd(int pid) throws IOException {
+ native_stopClatd(pid);
}
/**
@@ -843,9 +842,7 @@
Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
maybeStopBpf(mClatdTracker);
- mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
- mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
- mClatdTracker.pid);
+ mDeps.stopClatd(mClatdTracker.pid);
untagSocket(mClatdTracker.cookie);
Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
@@ -944,7 +941,6 @@
private static native int native_startClatd(FileDescriptor tunfd, FileDescriptor readsock6,
FileDescriptor writesock6, String iface, String pfx96, String v4, String v6)
throws IOException;
- private static native void native_stopClatd(String iface, String pfx96, String v4, String v6,
- int pid) throws IOException;
+ private static native void native_stopClatd(int pid) throws IOException;
private static native long native_getSocketCookie(FileDescriptor sock) throws IOException;
}
diff --git a/service/src/com/android/server/connectivity/NetworkDiagnostics.java b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
index 15d0925..4f80d47 100644
--- a/service/src/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,7 @@
import static android.system.OsConstants.*;
+import static com.android.net.module.util.NetworkStackConstants.DNS_OVER_TLS_PORT;
import static com.android.net.module.util.NetworkStackConstants.ICMP_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
@@ -730,7 +731,6 @@
private class DnsTlsCheck extends DnsUdpCheck {
private static final int TCP_CONNECT_TIMEOUT_MS = 2500;
private static final int TCP_TIMEOUT_MS = 2000;
- private static final int DNS_TLS_PORT = 853;
private static final int DNS_HEADER_SIZE = 12;
private final String mHostname;
@@ -769,7 +769,8 @@
final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
mMeasurement.startTime = now();
- sslSocket.connect(new InetSocketAddress(mTarget, DNS_TLS_PORT), TCP_CONNECT_TIMEOUT_MS);
+ sslSocket.connect(new InetSocketAddress(mTarget, DNS_OVER_TLS_PORT),
+ TCP_CONNECT_TIMEOUT_MS);
// Synchronous call waiting for the TLS handshake complete.
sslSocket.startHandshake();
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 12e7d33..2245382 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -30,7 +30,6 @@
"cts-net-utils",
"ctstestrunner-axt",
"modules-utils-build",
- "ub-uiautomator",
],
libs: [
"android.test.runner",
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
index b2e81ff..13bbab6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
@@ -19,18 +19,18 @@
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
import android.content.pm.PackageManager;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.UiAutomatorUtils;
+import com.android.compatibility.common.util.UiAutomatorUtils2;
import org.junit.After;
import org.junit.Assume;
@@ -84,7 +84,7 @@
final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
uiDevice.openNotification();
try {
- final UiObject2 uiObject = UiAutomatorUtils.waitFindObject(
+ final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(
By.text("Data warning"));
Assume.assumeNotNull(uiObject);
uiObject.wait(Until.clickable(true), 10_000L);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index c28ee64..73a6502 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -100,9 +100,6 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -114,6 +111,9 @@
import android.util.Range;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.modules.utils.build.SdkLevel;
@@ -154,7 +154,6 @@
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -809,26 +808,12 @@
mOldPrivateDnsSpecifier);
}
- // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
private void expectPrivateDnsHostname(final String hostname) throws Exception {
- final NetworkRequest request = new NetworkRequest.Builder()
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .build();
- final CountDownLatch latch = new CountDownLatch(1);
- final NetworkCallback callback = new NetworkCallback() {
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
- if (network.equals(mNetwork) &&
- Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
- latch.countDown();
- }
- }
- };
-
- registerNetworkCallback(request, callback);
-
- assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
+ // Wait for private DNS setting to propagate.
+ mCtsNetUtils.awaitPrivateDnsSetting("Test wait private DNS setting timeout",
+ network, hostname, false);
+ }
}
private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index f9fe5b0..51ee074 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -61,7 +61,7 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
- data: [":ConnectivityChecker"],
+ data: [":ConnectivityTestPreparer"],
per_testcase_directory: true,
host_required: ["net-tests-utils-host-common"],
test_config_template: "AndroidTestTemplate.xml",
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index d2fb04a..8efa99f 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -28,7 +28,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
- <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
<target_preparer class="com.android.testutils.DisableConfigSyncTargetPreparer">
</target_preparer>
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 50de714..ee2f6bb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2130,7 +2130,12 @@
@AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps")
@Test
public void testSetAirplaneMode() throws Exception{
- final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
+ // Starting from T, wifi supports airplane mode enhancement which may not disconnect wifi
+ // when airplane mode is on. The actual behavior that the device will have could only be
+ // checked with hidden wifi APIs(see Settings.Secure.WIFI_APM_STATE). Thus, stop verifying
+ // wifi on T+ device.
+ final boolean verifyWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+ && !SdkLevel.isAtLeastT();
final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
// store the current state of airplane mode
final boolean isAirplaneModeEnabled = isAirplaneModeEnabled();
@@ -2141,7 +2146,7 @@
// Verify that networks are available as expected if wifi or cell is supported. Continue the
// test if none of them are supported since test should still able to verify the permission
// mechanism.
- if (supportWifi) {
+ if (verifyWifi) {
mCtsNetUtils.ensureWifiConnected();
registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
}
@@ -2165,7 +2170,7 @@
// Verify that the enabling airplane mode takes effect as expected to prevent flakiness
// caused by fast airplane mode switches. Ensure network lost before turning off
// airplane mode.
- if (supportWifi) waitForLost(wifiCb);
+ if (verifyWifi) waitForLost(wifiCb);
if (supportTelephony) waitForLost(telephonyCb);
// Verify we can disable Airplane Mode with correct permission:
@@ -2174,7 +2179,7 @@
// Verify that turning airplane mode off takes effect as expected.
// connectToCell only registers a request, it cannot / does not need to be called twice
mCtsNetUtils.ensureWifiConnected();
- if (supportWifi) waitForAvailable(wifiCb);
+ if (verifyWifi) waitForAvailable(wifiCb);
if (supportTelephony) waitForAvailable(telephonyCb);
} finally {
// Restore the previous state of airplane mode and permissions:
@@ -2975,13 +2980,13 @@
allowBadWifi();
- final Network cellNetwork = mCtsNetUtils.connectToCell();
- final Network wifiNetwork = prepareValidatedNetwork();
-
- registerDefaultNetworkCallback(defaultCb);
- registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
-
try {
+ final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final Network wifiNetwork = prepareValidatedNetwork();
+
+ registerDefaultNetworkCallback(defaultCb);
+ registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+
// Verify wifi is the default network.
defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
entry -> wifiNetwork.equals(entry.getNetwork()));
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 691ab99..17a9ca2 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -18,21 +18,18 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkUtils;
import android.net.cts.util.CtsNetUtils;
import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.test.AndroidTestCase;
-import java.util.ArrayList;
-
public class MultinetworkApiTest extends AndroidTestCase {
static {
@@ -75,26 +72,8 @@
super.tearDown();
}
- private Network[] getTestableNetworks() {
- final ArrayList<Network> testableNetworks = new ArrayList<Network>();
- for (Network network : mCM.getAllNetworks()) {
- final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
- if (nc != null
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- testableNetworks.add(network);
- }
- }
-
- assertTrue(
- "This test requires that at least one network be connected. " +
- "Please ensure that the device is connected to a network.",
- testableNetworks.size() >= 1);
- return testableNetworks.toArray(new Network[0]);
- }
-
public void testGetaddrinfo() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runGetaddrinfoCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -109,7 +88,7 @@
assertNull(mCM.getProcessDefaultNetwork());
assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
mCM.setProcessDefaultNetwork(null);
assertNull(mCM.getProcessDefaultNetwork());
@@ -128,7 +107,7 @@
mCM.setProcessDefaultNetwork(null);
}
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
NetworkUtils.bindProcessToNetwork(0);
assertNull(mCM.getBoundNetworkForProcess());
@@ -148,7 +127,7 @@
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
public void testSetsocknetwork() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runSetsocknetwork(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -158,7 +137,7 @@
}
public void testNativeDatagramTransmission() throws ErrnoException {
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
int errno = runDatagramCheck(network.getNetworkHandle());
if (errno != 0) {
throw new ErrnoException(
@@ -181,7 +160,7 @@
public void testNetworkHandle() {
// Test Network -> NetworkHandle -> Network results in the same Network.
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
long networkHandle = network.getNetworkHandle();
Network newNetwork = Network.fromNetworkHandle(networkHandle);
assertEquals(newNetwork, network);
@@ -203,7 +182,7 @@
}
public void testResNApi() throws Exception {
- final Network[] testNetworks = getTestableNetworks();
+ final Network[] testNetworks = mCtsNetUtils.getTestableNetworks();
for (Network network : testNetworks) {
// Throws AssertionError directly in jni function if test fail.
@@ -229,7 +208,7 @@
// b/144521720
try {
mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
- for (Network network : getTestableNetworks()) {
+ for (Network network : mCtsNetUtils.getTestableNetworks()) {
// Wait for private DNS setting to propagate.
mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
network, GOOGLE_PRIVATE_DNS_SERVER, true);
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 869562b..af8938a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -29,9 +29,9 @@
import android.net.NattKeepalivePacketData
import android.net.Network
import android.net.NetworkAgent
-import android.net.NetworkAgentConfig
import android.net.NetworkAgent.INVALID_NETWORK
import android.net.NetworkAgent.VALID_NETWORK
+import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
@@ -46,21 +46,23 @@
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_TEST
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkInfo
import android.net.NetworkProvider
import android.net.NetworkReleasedException
import android.net.NetworkRequest
import android.net.NetworkScore
-import android.net.RouteInfo
import android.net.QosCallback
-import android.net.QosCallbackException
import android.net.QosCallback.QosCallbackRegistrationException
+import android.net.QosCallbackException
import android.net.QosSession
import android.net.QosSessionAttributes
import android.net.QosSocketInfo
+import android.net.RouteInfo
import android.net.SocketKeepalive
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
import android.net.Uri
import android.net.VpnManager
import android.net.VpnTransportInfo
@@ -71,6 +73,7 @@
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
+import android.os.Process
import android.os.SystemClock
import android.platform.test.annotations.AppModeFull
import android.system.OsConstants.IPPROTO_TCP
@@ -89,6 +92,7 @@
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
+import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
@@ -178,6 +182,7 @@
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Closeable? = null // either Socket or DatagramSocket
+ private val ifacesToCleanUp = mutableListOf<TestNetworkInterface>()
@Before
fun setUp() {
@@ -189,6 +194,7 @@
fun tearDown() {
agentsToCleanUp.forEach { it.unregister() }
callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
+ ifacesToCleanUp.forEach { it.fileDescriptor.close() }
qosTestSocket?.close()
mHandlerThread.quitSafely()
mHandlerThread.join()
@@ -269,7 +275,7 @@
removeCapability(NET_CAPABILITY_INTERNET)
addCapability(NET_CAPABILITY_NOT_SUSPENDED)
addCapability(NET_CAPABILITY_NOT_ROAMING)
- addCapability(NET_CAPABILITY_NOT_VPN)
+ if (!transports.contains(TRANSPORT_VPN)) addCapability(NET_CAPABILITY_NOT_VPN)
if (SdkLevel.isAtLeastS()) {
addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
}
@@ -304,7 +310,7 @@
context: Context = realContext,
specifier: String? = UUID.randomUUID().toString(),
initialConfig: NetworkAgentConfig? = null,
- expectedInitSignalStrengthThresholds: IntArray? = intArrayOf(),
+ expectedInitSignalStrengthThresholds: IntArray = intArrayOf(),
transports: IntArray = intArrayOf()
): Pair<TestableNetworkAgent, TestableNetworkCallback> {
val callback = TestableNetworkCallback()
@@ -317,8 +323,7 @@
agent.register()
agent.markConnected()
agent.expectCallback<OnNetworkCreated>()
- agent.expectSignalStrengths(expectedInitSignalStrengthThresholds)
- agent.expectValidationBypassedStatus()
+ agent.expectPostConnectionCallbacks(expectedInitSignalStrengthThresholds)
callback.expectAvailableThenValidatedCallbacks(agent.network!!)
return agent to callback
}
@@ -336,6 +341,19 @@
mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
}
+ private fun TestableNetworkAgent.expectPostConnectionCallbacks(
+ thresholds: IntArray = intArrayOf()
+ ) {
+ expectSignalStrengths(thresholds)
+ expectValidationBypassedStatus()
+ assertNoCallback()
+ }
+
+ private fun createTunInterface(): TestNetworkInterface = realContext.getSystemService(
+ TestNetworkManager::class.java)!!.createTunInterface(emptyList()).also {
+ ifacesToCleanUp.add(it)
+ }
+
fun assertLinkPropertiesEventually(
n: Network,
description: String,
@@ -1291,8 +1309,12 @@
requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
val agent6 = createNetworkAgent(specifier = specifier6)
val network6 = agent6.register()
- // No callbacks are sent, so check the LinkProperties to see if the network has connected.
- assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+ if (SdkLevel.isAtLeastU()) {
+ agent6.expectCallback<OnNetworkCreated>()
+ } else {
+ // No callbacks are sent, so check LinkProperties to wait for the network to be created.
+ assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+ }
// unregisterAfterReplacement tears down the network immediately.
// Approximately check that this is the case by picking an unregister timeout that's longer
@@ -1301,8 +1323,9 @@
val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
agent6.unregisterAfterReplacement(timeoutMs)
agent6.expectCallback<OnNetworkUnwanted>()
- if (!SdkLevel.isAtLeastT()) {
+ if (!SdkLevel.isAtLeastT() || SdkLevel.isAtLeastU()) {
// Before T, onNetworkDestroyed is called even if the network was never created.
+ // On U+, the network was created by register(). Destroying it sends onNetworkDestroyed.
agent6.expectCallback<OnNetworkDestroyed>()
}
// Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
@@ -1375,4 +1398,101 @@
callback.expect<Available>(agent.network!!)
callback.eventuallyExpect<Lost> { it.network == agent.network }
}
+
+ fun doTestNativeNetworkCreation(expectCreatedImmediately: Boolean, transports: IntArray) {
+ val iface = createTunInterface()
+ val ifName = iface.interfaceName
+ val nc = makeTestNetworkCapabilities(ifName, transports).also {
+ if (transports.contains(TRANSPORT_VPN)) {
+ val sessionId = "NetworkAgentTest-${Process.myPid()}"
+ it.transportInfo = VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
+ /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false)
+ it.underlyingNetworks = listOf()
+ }
+ }
+ val lp = LinkProperties().apply {
+ interfaceName = ifName
+ addLinkAddress(LinkAddress("2001:db8::1/64"))
+ addRoute(RouteInfo(IpPrefix("2001:db8::/64"), null /* nextHop */, ifName))
+ addRoute(RouteInfo(IpPrefix("::/0"),
+ InetAddresses.parseNumericAddress("fe80::abcd"),
+ ifName))
+ }
+
+ // File a request containing the agent's specifier to receive callbacks and to ensure that
+ // the agent is not torn down due to being unneeded.
+ val request = makeTestNetworkRequest(specifier = ifName)
+ val requestCallback = TestableNetworkCallback()
+ requestNetwork(request, requestCallback)
+
+ val listenCallback = TestableNetworkCallback()
+ registerNetworkCallback(request, listenCallback)
+
+ // Register the NetworkAgent...
+ val agent = createNetworkAgent(realContext, initialNc = nc, initialLp = lp)
+ val network = agent.register()
+
+ // ... and then change the NetworkCapabilities and LinkProperties.
+ nc.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ agent.sendNetworkCapabilities(nc)
+ lp.addLinkAddress(LinkAddress("192.0.2.2/25"))
+ lp.addRoute(RouteInfo(IpPrefix("192.0.2.0/25"), null /* nextHop */, ifName))
+ agent.sendLinkProperties(lp)
+
+ requestCallback.assertNoCallback()
+ listenCallback.assertNoCallback()
+ if (!expectCreatedImmediately) {
+ agent.assertNoCallback()
+ agent.markConnected()
+ agent.expectCallback<OnNetworkCreated>()
+ } else {
+ agent.expectCallback<OnNetworkCreated>()
+ agent.markConnected()
+ }
+ agent.expectPostConnectionCallbacks()
+
+ // onAvailable must be called only when the network connects, and no other callbacks may be
+ // called before that happens. The callbacks report the state of the network as it was when
+ // it connected, so they reflect the NC and LP changes made after registration.
+ requestCallback.expect<Available>(network)
+ listenCallback.expect<Available>(network)
+
+ requestCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+ listenCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+
+ requestCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+ listenCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+
+ requestCallback.expect<BlockedStatus>()
+ listenCallback.expect<BlockedStatus>()
+
+ // Except for network validation, ensure no more callbacks are sent.
+ requestCallback.expectCaps(network) {
+ it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ listenCallback.expectCaps(network) {
+ it.hasCapability(NET_CAPABILITY_VALIDATED)
+ }
+ unregister(agent)
+ // Lost implicitly checks that no further callbacks happened after connect.
+ requestCallback.expect<Lost>(network)
+ listenCallback.expect<Lost>(network)
+ assertNull(mCM.getLinkProperties(network))
+ }
+
+ @Test
+ fun testNativeNetworkCreation_PhysicalNetwork() {
+ // On T and below, the native network is only created when the agent connects.
+ // Starting in U, the native network is created as soon as the agent is registered.
+ doTestNativeNetworkCreation(expectCreatedImmediately = SdkLevel.isAtLeastU(),
+ intArrayOf(TRANSPORT_CELLULAR))
+ }
+
+ @Test
+ fun testNativeNetworkCreation_Vpn() {
+ // VPN networks are always created as soon as the agent is registered.
+ doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index fcfecad..2704dd3 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -30,6 +30,7 @@
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
+import android.util.Log
import androidx.test.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -41,6 +42,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.Collections
// This test doesn't really have a constraint on how fast the methods should return. If it's
// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
@@ -64,10 +66,11 @@
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner::class)
class NetworkScoreTest {
+ private val TAG = javaClass.simpleName
private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
- private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+ private val mHandlerThread = HandlerThread("$TAG handler thread")
private val mHandler by lazy { Handler(mHandlerThread.looper) }
- private val agentsToCleanUp = mutableListOf<NetworkAgent>()
+ private val agentsToCleanUp = Collections.synchronizedList(mutableListOf<NetworkAgent>())
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
@Before
@@ -83,15 +86,18 @@
.addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler
)
}
+ Log.i(TAG, "Teardown on thread ${System.identityHashCode(Thread.currentThread())} " +
+ "cleaning up ${agentsToCleanUp.size} agents")
agentsToCleanUp.forEach {
+ Log.i(TAG, "Unregister agent for net ${it.network}")
it.unregister()
agentCleanUpCb.eventuallyExpect<CallbackEntry.Lost> { cb -> cb.network == it.network }
}
mCm.unregisterNetworkCallback(agentCleanUpCb)
+ callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
mHandlerThread.quitSafely()
mHandlerThread.join()
- callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
}
// Returns a networkCallback that sends onAvailable on the best network with TRANSPORT_TEST.
@@ -145,6 +151,8 @@
val agent = object : NetworkAgent(context, looper, "NetworkScore test agent", nc,
LinkProperties(), score, config, NetworkProvider(context, looper,
"NetworkScore test provider")) {}.also {
+ Log.i(TAG, "Add on thread ${System.identityHashCode(Thread.currentThread())} " +
+ "agent to clean up $it")
agentsToCleanUp.add(it)
}
runWithShellPermissionIdentity({ agent.register() }, MANAGE_TEST_NETWORKS)
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index f86c5cd..d8a0b07 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -210,7 +210,6 @@
private long mStartTime;
private long mEndTime;
- private long mBytesRead;
private String mWriteSettingsMode;
private String mUsageStatsMode;
@@ -229,6 +228,7 @@
TrafficStats.setThreadStatsTag(NETWORK_TAG);
urlc = (HttpURLConnection) network.openConnection(url);
urlc.setConnectTimeout(TIMEOUT_MILLIS);
+ urlc.setReadTimeout(TIMEOUT_MILLIS);
urlc.setUseCaches(false);
// Disable compression so we generate enough traffic that assertWithinPercentage will
// not be affected by the small amount of traffic (5-10kB) sent by the test harness.
@@ -236,11 +236,10 @@
urlc.connect();
boolean ping = urlc.getResponseCode() == 200;
if (ping) {
- in = new InputStreamReader(
- (InputStream) urlc.getContent());
-
- mBytesRead = 0;
- while (in.read() != -1) ++mBytesRead;
+ in = new InputStreamReader((InputStream) urlc.getContent());
+ // Since the test doesn't really care about the precise amount of data, instead
+ // of reading all contents, just read few bytes at the beginning.
+ in.read();
}
} catch (Exception e) {
Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
@@ -379,7 +378,7 @@
.build(), callback);
synchronized (this) {
try {
- wait((int) (TIMEOUT_MILLIS * 1.2));
+ wait((int) (TIMEOUT_MILLIS * 2.4));
} catch (InterruptedException e) {
}
}
@@ -394,7 +393,7 @@
assertFalse(mNetworkInterfacesToTest[networkTypeIndex].getSystemFeature()
+ " is a reported system feature, "
+ "however no corresponding connected network interface was found or the attempt "
- + "to connect has timed out (timeout = " + TIMEOUT_MILLIS + "ms)."
+ + "to connect and read has timed out (timeout = " + (TIMEOUT_MILLIS * 2) + "ms)."
+ mNetworkInterfacesToTest[networkTypeIndex].getErrorMessage(), hasFeature);
return false;
}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 9d73946..ce789fc 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -57,6 +57,8 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -68,6 +70,8 @@
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -508,17 +512,18 @@
* @throws InterruptedException If the thread is interrupted.
*/
public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
- @NonNull String server, boolean requiresValidatedServer) throws InterruptedException {
+ @Nullable String server, boolean requiresValidatedServer) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
- NetworkCallback callback = new NetworkCallback() {
+ final NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
return;
}
- if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
+ Log.i(TAG, "Set private DNS server to " + server);
+ if (network.equals(n) && Objects.equals(server, lp.getPrivateDnsServerName())) {
latch.countDown();
}
}
@@ -541,6 +546,27 @@
}
/**
+ * Get all testable Networks with internet capability.
+ */
+ public Network[] getTestableNetworks() {
+ final ArrayList<Network> testableNetworks = new ArrayList<Network>();
+ for (Network network : mCm.getAllNetworks()) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc != null
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ testableNetworks.add(network);
+ }
+ }
+
+ assertTrue("This test requires that at least one public Internet-providing"
+ + " network be connected. Please ensure that the device is connected to"
+ + " a network.",
+ testableNetworks.size() >= 1);
+ return testableNetworks.toArray(new Network[0]);
+ }
+
+ /**
* Receiver that captures the last connectivity change's network type and state. Recognizes
* both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
*/
diff --git a/tests/cts/tethering/AndroidTestTemplate.xml b/tests/cts/tethering/AndroidTestTemplate.xml
index c842c09..9b33afe 100644
--- a/tests/cts/tethering/AndroidTestTemplate.xml
+++ b/tests/cts/tethering/AndroidTestTemplate.xml
@@ -25,7 +25,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
- <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ <target_preparer class="com.android.testutils.ConnectivityTestTargetPreparer">
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.tethering.cts" />
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 8396252..a0d0647 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -3815,6 +3815,12 @@
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, callbacks);
+ if (mService.shouldCreateNetworksImmediately()) {
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } else {
+ assertNull(eventOrder.poll());
+ }
+
// Connect a network, and file a request for it after it has come up, to ensure the nascent
// timer is cleared and the test does not have to wait for it. Filing the request after the
// network has come up is necessary because ConnectivityService does not appear to clear the
@@ -3822,7 +3828,12 @@
// connected.
// TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
mWiFiAgent.connectWithoutInternet();
- waitForIdle();
+ if (!mService.shouldCreateNetworksImmediately()) {
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } else {
+ waitForIdle();
+ assertNull(eventOrder.poll());
+ }
mCm.requestNetwork(request, callback);
callback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
@@ -3839,7 +3850,6 @@
// Disconnect the network and check that events happened in the right order.
mCm.unregisterNetworkCallback(callback);
- assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -7625,7 +7635,9 @@
// Simple connection with initial LP should have updated ifaces.
mCellAgent.connect(false);
waitForIdle();
- expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
+ List<Network> allNetworks = mService.shouldCreateNetworksImmediately()
+ ? cellAndWifi() : onlyCell();
+ expectNotifyNetworkStatus(allNetworks, onlyCell(), MOBILE_IFNAME);
reset(mStatsManager);
// Verify change fields other than interfaces does not trigger a notification to NSS.
@@ -7934,9 +7946,13 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final int netId = mCellAgent.getNetwork().netId;
waitForIdle();
- verify(mMockDnsResolver, never()).setResolverConfiguration(any());
- verifyNoMoreInteractions(mMockDnsResolver);
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ } else {
+ verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+ }
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -7952,10 +7968,13 @@
mCellAgent.sendLinkProperties(cellLp);
mCellAgent.connect(false);
waitForIdle();
-
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
- // CS tells dnsresolver about the empty DNS config for this network.
+ if (!mService.shouldCreateNetworksImmediately()) {
+ // CS tells dnsresolver about the empty DNS config for this network.
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ }
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
+
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockDnsResolver);
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
@@ -8070,10 +8089,13 @@
mCm.requestNetwork(cellRequest, cellNetworkCallback);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final int netId = mCellAgent.getNetwork().netId;
waitForIdle();
- // CS tells netd about the empty DNS config for this network.
- verify(mMockDnsResolver, never()).setResolverConfiguration(any());
- verifyNoMoreInteractions(mMockDnsResolver);
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ } else {
+ verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+ }
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -8092,7 +8114,9 @@
mCellAgent.sendLinkProperties(cellLp);
mCellAgent.connect(false);
waitForIdle();
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
+ if (!mService.shouldCreateNetworksImmediately()) {
+ verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+ }
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
mResolverParamsParcelCaptor.capture());
ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue();
@@ -8103,6 +8127,7 @@
assertEquals(2, resolvrParams.tlsServers.length);
assertTrue(new ArraySet<>(resolvrParams.tlsServers).containsAll(
asList("2001:db8::1", "192.0.2.1")));
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockDnsResolver);
cellNetworkCallback.expect(AVAILABLE, mCellAgent);
cellNetworkCallback.expect(NETWORK_CAPS_UPDATED, mCellAgent);
@@ -10474,7 +10499,8 @@
if (inOrder != null) {
return inOrder.verify(t);
} else {
- return verify(t);
+ // times(1) for consistency with the above. InOrder#verify always implies times(1).
+ return verify(t, times(1));
}
}
@@ -10523,6 +10549,21 @@
}
}
+ private void expectNativeNetworkCreated(int netId, int permission, String iface,
+ InOrder inOrder) throws Exception {
+ verifyWithOrder(inOrder, mMockNetd).networkCreate(nativeNetworkConfigPhysical(netId,
+ permission));
+ verifyWithOrder(inOrder, mMockDnsResolver).createNetworkCache(eq(netId));
+ if (iface != null) {
+ verifyWithOrder(inOrder, mMockNetd).networkAddInterface(netId, iface);
+ }
+ }
+
+ private void expectNativeNetworkCreated(int netId, int permission, String iface)
+ throws Exception {
+ expectNativeNetworkCreated(netId, permission, iface, null /* inOrder */);
+ }
+
@Test
public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -10560,11 +10601,8 @@
int cellNetId = mCellAgent.getNetwork().netId;
waitForIdle();
- verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
- INetd.PERMISSION_NONE));
+ expectNativeNetworkCreated(cellNetId, INetd.PERMISSION_NONE, MOBILE_IFNAME);
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
- verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
- verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
mDeps.mReportedInterfaceHistory.newReadHead();
assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
@@ -15111,7 +15149,7 @@
UserHandle testHandle,
TestNetworkCallback profileDefaultNetworkCallback,
TestNetworkCallback disAllowProfileDefaultNetworkCallback) throws Exception {
- final InOrder inOrder = inOrder(mMockNetd);
+ final InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellAgent.connect(true);
@@ -15127,8 +15165,16 @@
final TestNetworkAgentWrapper workAgent =
makeEnterpriseNetworkAgent(profileNetworkPreference.getPreferenceEnterpriseId());
+ if (mService.shouldCreateNetworksImmediately()) {
+ expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+ null /* iface */, inOrder);
+ }
if (connectWorkProfileAgentAhead) {
workAgent.connect(false);
+ if (!mService.shouldCreateNetworksImmediately()) {
+ expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+ null /* iface */, inOrder);
+ }
}
final TestOnCompleteListener listener = new TestOnCompleteListener();
@@ -15168,6 +15214,11 @@
if (!connectWorkProfileAgentAhead) {
workAgent.connect(false);
+ if (!mService.shouldCreateNetworksImmediately()) {
+ inOrder.verify(mMockNetd).networkCreate(
+ nativeNetworkConfigPhysical(workAgent.getNetwork().netId,
+ INetd.PERMISSION_SYSTEM));
+ }
}
profileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
@@ -15176,8 +15227,6 @@
}
mSystemDefaultNetworkCallback.assertNoCallback();
mDefaultNetworkCallback.assertNoCallback();
- inOrder.verify(mMockNetd).networkCreate(
- nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
workAgent.getNetwork().netId,
uidRangeFor(testHandle, profileNetworkPreference),
@@ -17696,6 +17745,22 @@
verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
}
+ private void verifyMtuSetOnWifiInterfaceOnlyUpToT(int mtu) throws Exception {
+ if (!mService.shouldCreateNetworksImmediately()) {
+ verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+ } else {
+ verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ }
+ }
+
+ private void verifyMtuSetOnWifiInterfaceOnlyStartingFromU(int mtu) throws Exception {
+ if (mService.shouldCreateNetworksImmediately()) {
+ verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+ } else {
+ verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+ }
+ }
+
@Test
public void testSendLinkPropertiesSetInterfaceMtuBeforeConnect() throws Exception {
final int mtu = 1281;
@@ -17710,8 +17775,8 @@
reset(mMockNetd);
mWiFiAgent.connect(false /* validated */);
- // The MTU is always (re-)applied when the network connects.
- verifyMtuSetOnWifiInterface(mtu);
+ // Before U, the MTU is always (re-)applied when the network connects.
+ verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu);
}
@Test
@@ -17721,13 +17786,13 @@
lp.setInterfaceName(WIFI_IFNAME);
lp.setMtu(mtu);
- // Registering an agent with an MTU doesn't set the MTU...
+ // Registering an agent with an MTU only sets the MTU on U+.
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
waitForIdle();
- verifyMtuNeverSetOnWifiInterface();
+ verifyMtuSetOnWifiInterfaceOnlyStartingFromU(mtu);
reset(mMockNetd);
- // ... but prevents future updates with the same MTU from setting the MTU.
+ // Future updates with the same MTU don't set the MTU even on T when it's not set initially.
mWiFiAgent.sendLinkProperties(lp);
waitForIdle();
verifyMtuNeverSetOnWifiInterface();
@@ -17740,8 +17805,8 @@
reset(mMockNetd);
mWiFiAgent.connect(false /* validated */);
- // The MTU is always (re-)applied when the network connects.
- verifyMtuSetOnWifiInterface(mtu + 1);
+ // Before U, the MTU is always (re-)applied when the network connects.
+ verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu + 1);
}
@Test
@@ -17922,4 +17987,34 @@
verify(mDeps).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
eq(exemptUids));
}
+
+ @Test
+ public void testDisconnectSuspendedNetworkStopClatd() throws Exception {
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_DUN)
+ .build();
+ mCm.requestNetwork(networkRequest, networkCallback);
+
+ final IpPrefix nat64Prefix = new IpPrefix(InetAddress.getByName("64:ff9b::"), 96);
+ NetworkCapabilities nc = new NetworkCapabilities().addCapability(NET_CAPABILITY_DUN);
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(MOBILE_IFNAME);
+ lp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+ lp.setNat64Prefix(nat64Prefix);
+ mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, lp, nc);
+ mCellAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* privateDnsProbeSent */);
+
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, mCellAgent.getNetwork().netId,
+ nat64Prefix.toString());
+
+ mCellAgent.suspend();
+ mCm.unregisterNetworkCallback(networkCallback);
+ mCellAgent.expectDisconnected();
+ waitForIdle();
+
+ // TODO (aosp/2583410): update following check to verifyClatdStop
+ verifyNeverClatdStop(null /* inOrder */, MOBILE_IFNAME);
+ }
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index b3e8cc8..955be12 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -178,10 +178,10 @@
doReturn(true).when(mMockMDnsM).resolve(
anyInt(), anyString(), anyString(), anyString(), anyInt());
doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
- doReturn(mDiscoveryManager).when(mDeps).makeMdnsDiscoveryManager(any(), any(), any());
+ doReturn(mDiscoveryManager).when(mDeps)
+ .makeMdnsDiscoveryManager(any(), any(), any(), any());
doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any());
doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any());
-
mService = makeService();
}
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index b651c33..4158663 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -313,8 +313,7 @@
* Stop clatd.
*/
@Override
- public void stopClatd(@NonNull String iface, @NonNull String pfx96, @NonNull String v4,
- @NonNull String v6, int pid) throws IOException {
+ public void stopClatd(int pid) throws IOException {
if (pid == -1) {
fail("unsupported arg: " + pid);
}
@@ -479,8 +478,7 @@
eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
- inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
- eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
+ inOrder.verify(mDeps).stopClatd(eq(CLATD_PID));
inOrder.verify(mCookieTagMap).deleteEntry(eq(COOKIE_TAG_KEY));
assertNull(coordinator.getClatdTrackerForTesting());
inOrder.verifyNoMoreInteractions();
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
index b539fe0..d9acc61 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -51,6 +51,7 @@
private const val SERVICE_ID_2 = 2
private const val LONG_SERVICE_ID_1 = 3
private const val LONG_SERVICE_ID_2 = 4
+private const val CASE_INSENSITIVE_TEST_SERVICE_ID = 5
private const val TIMEOUT_MS = 10_000L
private val TEST_ADDR = parseNumericAddress("2001:db8::123")
private val TEST_LINKADDR = LinkAddress(TEST_ADDR, 64 /* prefixLength */)
@@ -78,6 +79,13 @@
network = null
}
+private val ALL_NETWORKS_SERVICE_2 =
+ NsdServiceInfo("TESTSERVICENAME", "_ADVERTISERTEST._tcp").apply {
+ port = 12345
+ hostAddresses = listOf(TEST_ADDR)
+ network = null
+ }
+
private val LONG_ALL_NETWORKS_SERVICE =
NsdServiceInfo("a".repeat(48) + "TestServiceName", "_longadvertisertest._tcp").apply {
port = 12345
@@ -228,6 +236,9 @@
postSync { advertiser.addService(LONG_SERVICE_ID_2, LONG_ALL_NETWORKS_SERVICE,
null /* subtype */) }
+ postSync { advertiser.addService(CASE_INSENSITIVE_TEST_SERVICE_ID, ALL_NETWORKS_SERVICE_2,
+ null /* subtype */) }
+
// Callbacks for matching network and all networks both get the socket
postSync {
oneNetSocketCb.onSocketCreated(TEST_NETWORK_1, mockSocket1, listOf(TEST_LINKADDR))
@@ -249,6 +260,14 @@
network = LONG_ALL_NETWORKS_SERVICE.network
}
+ val expectedCaseInsensitiveRenamed = NsdServiceInfo(
+ "${ALL_NETWORKS_SERVICE_2.serviceName} (3)", ALL_NETWORKS_SERVICE_2.serviceType
+ ).apply {
+ port = ALL_NETWORKS_SERVICE_2.port
+ hostAddresses = ALL_NETWORKS_SERVICE_2.hostAddresses
+ network = ALL_NETWORKS_SERVICE_2.network
+ }
+
val intAdvCbCaptor = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java)
verify(mockDeps).makeAdvertiser(eq(mockSocket1), eq(listOf(TEST_LINKADDR)),
eq(thread.looper), any(), intAdvCbCaptor.capture(), eq(TEST_HOSTNAME), any()
@@ -261,6 +280,8 @@
argThat { it.matches(LONG_SERVICE_1) }, eq(null))
verify(mockInterfaceAdvertiser1).addService(eq(LONG_SERVICE_ID_2),
argThat { it.matches(expectedLongRenamed) }, eq(null))
+ verify(mockInterfaceAdvertiser1).addService(eq(CASE_INSENSITIVE_TEST_SERVICE_ID),
+ argThat { it.matches(expectedCaseInsensitiveRenamed) }, eq(null))
doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1)
postSync { intAdvCbCaptor.value.onRegisterServiceSucceeded(
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 63357f1..89776e2 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -27,6 +27,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Pair;
@@ -34,7 +36,9 @@
import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,7 +57,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
public class MdnsDiscoveryManagerTests {
-
+ private static final long DEFAULT_TIMEOUT = 2000L;
private static final String SERVICE_TYPE_1 = "_googlecast._tcp.local";
private static final String SERVICE_TYPE_2 = "_test._tcp.local";
private static final Network NETWORK_1 = Mockito.mock(Network.class);
@@ -78,12 +82,18 @@
@Mock MdnsServiceBrowserListener mockListenerTwo;
@Mock SharedLog sharedLog;
private MdnsDiscoveryManager discoveryManager;
+ private HandlerThread thread;
+ private Handler handler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog) {
+ thread = new HandlerThread("MdnsDiscoveryManagerTests");
+ thread.start();
+ handler = new Handler(thread.getLooper());
+ discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog,
+ thread.getLooper()) {
@Override
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@Nullable Network network) {
@@ -103,11 +113,23 @@
};
}
+ @After
+ public void tearDown() {
+ if (thread != null) {
+ thread.quitSafely();
+ }
+ }
+
+ private void runOnHandler(Runnable r) {
+ handler.post(r);
+ HandlerUtils.waitForIdle(handler, DEFAULT_TIMEOUT);
+ }
+
private SocketCreationCallback expectSocketCreationCallback(String serviceType,
MdnsServiceBrowserListener listener, MdnsSearchOptions options) throws IOException {
final ArgumentCaptor<SocketCreationCallback> callbackCaptor =
ArgumentCaptor.forClass(SocketCreationCallback.class);
- discoveryManager.registerListener(serviceType, listener, options);
+ runOnHandler(() -> discoveryManager.registerListener(serviceType, listener, options));
verify(socketClient).startDiscovery();
verify(socketClient).notifyNetworkRequested(
eq(listener), eq(options.getNetwork()), callbackCaptor.capture());
@@ -120,11 +142,11 @@
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options);
- callback.onSocketCreated(null /* network */);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
verify(mockServiceTypeClientOne).startSendAndReceive(mockListenerOne, options);
when(mockServiceTypeClientOne.stopSendAndReceive(mockListenerOne)).thenReturn(true);
- discoveryManager.unregisterListener(SERVICE_TYPE_1, mockListenerOne);
+ runOnHandler(() -> discoveryManager.unregisterListener(SERVICE_TYPE_1, mockListenerOne));
verify(mockServiceTypeClientOne).stopSendAndReceive(mockListenerOne);
verify(socketClient).stopDiscovery();
}
@@ -135,16 +157,16 @@
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options);
- callback.onSocketCreated(null /* network */);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
verify(mockServiceTypeClientOne).startSendAndReceive(mockListenerOne, options);
- callback.onSocketCreated(NETWORK_1);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
verify(mockServiceTypeClientOne1).startSendAndReceive(mockListenerOne, options);
final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, options);
- callback2.onSocketCreated(null /* network */);
+ runOnHandler(() -> callback2.onSocketCreated(null /* network */));
verify(mockServiceTypeClientTwo).startSendAndReceive(mockListenerTwo, options);
- callback2.onSocketCreated(NETWORK_2);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_2));
verify(mockServiceTypeClientTwo2).startSendAndReceive(mockListenerTwo, options);
}
@@ -154,21 +176,22 @@
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options1);
- callback.onSocketCreated(null /* network */);
+ runOnHandler(() -> callback.onSocketCreated(null /* network */));
verify(mockServiceTypeClientOne).startSendAndReceive(mockListenerOne, options1);
- callback.onSocketCreated(NETWORK_1);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
verify(mockServiceTypeClientOne1).startSendAndReceive(mockListenerOne, options1);
final MdnsSearchOptions options2 =
MdnsSearchOptions.newBuilder().setNetwork(NETWORK_2).build();
final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, options2);
- callback2.onSocketCreated(NETWORK_2);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_2));
verify(mockServiceTypeClientTwo2).startSendAndReceive(mockListenerTwo, options2);
final MdnsPacket responseForServiceTypeOne = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1;
- discoveryManager.onResponseReceived(responseForServiceTypeOne, ifIndex, null /* network */);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForServiceTypeOne, ifIndex, null /* network */));
verify(mockServiceTypeClientOne).processResponse(responseForServiceTypeOne, ifIndex,
null /* network */);
verify(mockServiceTypeClientOne1).processResponse(responseForServiceTypeOne, ifIndex,
@@ -177,7 +200,8 @@
null /* network */);
final MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2);
- discoveryManager.onResponseReceived(responseForServiceTypeTwo, ifIndex, NETWORK_1);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForServiceTypeTwo, ifIndex, NETWORK_1));
verify(mockServiceTypeClientOne).processResponse(responseForServiceTypeTwo, ifIndex,
NETWORK_1);
verify(mockServiceTypeClientOne1).processResponse(responseForServiceTypeTwo, ifIndex,
@@ -187,7 +211,8 @@
final MdnsPacket responseForSubtype =
createMdnsPacket("subtype._sub._googlecast._tcp.local");
- discoveryManager.onResponseReceived(responseForSubtype, ifIndex, NETWORK_2);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ responseForSubtype, ifIndex, NETWORK_2));
verify(mockServiceTypeClientOne).processResponse(responseForSubtype, ifIndex, NETWORK_2);
verify(mockServiceTypeClientOne1, never()).processResponse(
responseForSubtype, ifIndex, NETWORK_2);
@@ -201,7 +226,7 @@
MdnsSearchOptions.newBuilder().setNetwork(NETWORK_1).build();
final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options1);
- callback.onSocketCreated(NETWORK_1);
+ runOnHandler(() -> callback.onSocketCreated(NETWORK_1));
verify(mockServiceTypeClientOne1).startSendAndReceive(mockListenerOne, options1);
// Create a ServiceTypeClient for SERVICE_TYPE_2 and NETWORK_2
@@ -209,26 +234,28 @@
MdnsSearchOptions.newBuilder().setNetwork(NETWORK_2).build();
final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, options2);
- callback2.onSocketCreated(NETWORK_2);
+ runOnHandler(() -> callback2.onSocketCreated(NETWORK_2));
verify(mockServiceTypeClientTwo2).startSendAndReceive(mockListenerTwo, options2);
// Receive a response, it should be processed on both clients.
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1;
- discoveryManager.onResponseReceived(response, ifIndex, null /* network */);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, null /* network */));
verify(mockServiceTypeClientOne1).processResponse(response, ifIndex, null /* network */);
verify(mockServiceTypeClientTwo2).processResponse(response, ifIndex, null /* network */);
// The client for NETWORK_1 receives the callback that the NETWORK_1 has been destroyed,
// mockServiceTypeClientOne1 should send service removed notifications and remove from the
// list of clients.
- callback.onSocketDestroyed(NETWORK_1);
- verify(mockServiceTypeClientOne1).notifyAllServicesRemoved();
+ runOnHandler(() -> callback.onAllSocketsDestroyed(NETWORK_1));
+ verify(mockServiceTypeClientOne1).notifySocketDestroyed();
// Receive a response again, it should be processed only on mockServiceTypeClientTwo2.
// Because the mockServiceTypeClientOne1 is removed from the list of clients, it is no
// longer able to process responses.
- discoveryManager.onResponseReceived(response, ifIndex, null /* network */);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, null /* network */));
verify(mockServiceTypeClientOne1, times(1))
.processResponse(response, ifIndex, null /* network */);
verify(mockServiceTypeClientTwo2, times(2))
@@ -236,12 +263,13 @@
// The client for NETWORK_2 receives the callback that the NETWORK_1 has been destroyed,
// mockServiceTypeClientTwo2 shouldn't send any notifications.
- callback2.onSocketDestroyed(NETWORK_1);
- verify(mockServiceTypeClientTwo2, never()).notifyAllServicesRemoved();
+ runOnHandler(() -> callback2.onAllSocketsDestroyed(NETWORK_1));
+ verify(mockServiceTypeClientTwo2, never()).notifySocketDestroyed();
// Receive a response again, mockServiceTypeClientTwo2 is still in the list of clients, it's
// still able to process responses.
- discoveryManager.onResponseReceived(response, ifIndex, null /* network */);
+ runOnHandler(() -> discoveryManager.onResponseReceived(
+ response, ifIndex, null /* network */));
verify(mockServiceTypeClientOne1, times(1))
.processResponse(response, ifIndex, null /* network */);
verify(mockServiceTypeClientTwo2, times(3))
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
index 90c43e5..c6137c6 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -24,8 +24,12 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.net.InetAddresses;
import android.net.Network;
@@ -77,12 +81,17 @@
}
private SocketCallback expectSocketCallback() {
+ return expectSocketCallback(mListener, mNetwork);
+ }
+
+ private SocketCallback expectSocketCallback(MdnsServiceBrowserListener listener,
+ Network requestedNetwork) {
final ArgumentCaptor<SocketCallback> callbackCaptor =
ArgumentCaptor.forClass(SocketCallback.class);
mHandler.post(() -> mSocketClient.notifyNetworkRequested(
- mListener, mNetwork, mSocketCreationCallback));
+ listener, requestedNetwork, mSocketCreationCallback));
verify(mProvider, timeout(DEFAULT_TIMEOUT))
- .requestSocket(eq(mNetwork), callbackCaptor.capture());
+ .requestSocket(eq(requestedNetwork), callbackCaptor.capture());
return callbackCaptor.getValue();
}
@@ -169,4 +178,103 @@
new String[] { "Android", "local" } /* serviceHost */)
), response.answers);
}
+
+ @Test
+ public void testSocketRemovedAfterNetworkUnrequested() throws IOException {
+ // Request a socket
+ final SocketCallback callback = expectSocketCallback(mListener, mNetwork);
+ final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length,
+ InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */);
+ doReturn(true).when(mSocket).hasJoinedIpv4();
+ doReturn(true).when(mSocket).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ // Notify socket created
+ callback.onSocketCreated(mNetwork, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(mNetwork);
+
+ // Send IPv4 packet and verify sending has been called.
+ mSocketClient.sendMulticastPacket(ipv4Packet);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket).send(ipv4Packet);
+
+ // Request another socket with null network
+ final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class);
+ final Network network2 = mock(Network.class);
+ final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class);
+ final SocketCallback callback2 = expectSocketCallback(listener2, null);
+ doReturn(true).when(socket2).hasJoinedIpv4();
+ doReturn(true).when(socket2).hasJoinedIpv6();
+ doReturn(createEmptyNetworkInterface()).when(socket2).getInterface();
+ // Notify socket created for two networks.
+ callback2.onSocketCreated(mNetwork, mSocket, List.of());
+ callback2.onSocketCreated(network2, socket2, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(mNetwork);
+ verify(mSocketCreationCallback).onSocketCreated(network2);
+
+ // Send IPv4 packet and verify sending to two sockets.
+ mSocketClient.sendMulticastPacket(ipv4Packet);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket, times(2)).send(ipv4Packet);
+ verify(socket2).send(ipv4Packet);
+
+ // Unrequest another socket
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2));
+ verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2);
+
+ // Send IPv4 packet again and verify only sending via mSocket
+ mSocketClient.sendMulticastPacket(ipv4Packet);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket, times(3)).send(ipv4Packet);
+ verify(socket2).send(ipv4Packet);
+
+ // Unrequest remaining socket
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
+ verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback);
+
+ // Send IPv4 packet and verify no more sending.
+ mSocketClient.sendMulticastPacket(ipv4Packet);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mSocket, times(3)).send(ipv4Packet);
+ verify(socket2).send(ipv4Packet);
+ }
+
+ @Test
+ public void testNotifyNetworkUnrequested_SocketsOnNullNetwork() {
+ final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketCallback callback = expectSocketCallback(
+ mListener, null /* requestedNetwork */);
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
+
+ callback.onSocketCreated(null /* network */, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(null);
+ callback.onSocketCreated(null /* network */, otherSocket, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
+
+ verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(null /* network */);
+ mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+ verify(mProvider).unrequestSocket(callback);
+ verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */);
+ }
+
+ @Test
+ public void testSocketCreatedAndDestroyed_NullNetwork() throws IOException {
+ final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketCallback callback = expectSocketCallback(mListener, null /* network */);
+ doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+ doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
+
+ callback.onSocketCreated(null /* network */, mSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(null);
+ callback.onSocketCreated(null /* network */, otherSocket, List.of());
+ verify(mSocketCreationCallback, times(2)).onSocketCreated(null);
+
+ // Notify socket destroyed
+ callback.onInterfaceDestroyed(null /* network */, mSocket);
+ verifyNoMoreInteractions(mSocketCreationCallback);
+ callback.onInterfaceDestroyed(null /* network */, otherSocket);
+ verify(mSocketCreationCallback).onAllSocketsDestroyed(null /* network */);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
index 5c9c294..a545373 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsPacketWriterTest.kt
@@ -31,7 +31,7 @@
@Test
fun testNameCompression() {
val writer = MdnsPacketWriter(ByteArray(1000))
- writer.writeLabels(arrayOf("my", "first", "name"))
+ writer.writeLabels(arrayOf("my", "FIRST", "name"))
writer.writeLabels(arrayOf("my", "second", "name"))
writer.writeLabels(arrayOf("other", "first", "name"))
writer.writeLabels(arrayOf("my", "second", "name"))
@@ -41,7 +41,7 @@
InetSocketAddress(InetAddresses.parseNumericAddress("2001:db8::123"), 123))
// Each label takes length + 1. So "first.name" offset = 3, "name" offset = 9
- val expected = "my".label() + "first".label() + "name".label() + 0x00.toByte() +
+ val expected = "my".label() + "FIRST".label() + "name".label() + 0x00.toByte() +
// "my.second.name" offset = 15
"my".label() + "second".label() + byteArrayOf(0xC0.toByte(), 9) +
"other".label() + byteArrayOf(0xC0.toByte(), 3) +
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
index 2b5423b..0a8d78d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsProberTest.kt
@@ -48,6 +48,7 @@
private val TEST_SERVICE_NAME_1 = arrayOf("testservice", "_nmt", "_tcp", "local")
private val TEST_SERVICE_NAME_2 = arrayOf("testservice2", "_nmt", "_tcp", "local")
+private val TEST_SERVICE_NAME_3 = arrayOf("Testservice", "_nmt", "_tcp", "local")
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
@@ -129,6 +130,15 @@
}
@Test
+ fun testCreateProberCaseInsensitive() {
+ val probeInfo = TestProbeInfo(
+ listOf(makeServiceRecord(TEST_SERVICE_NAME_1, 37890),
+ makeServiceRecord(TEST_SERVICE_NAME_2, 37890),
+ makeServiceRecord(TEST_SERVICE_NAME_3, 37890)))
+ assertEquals(2, probeInfo.getPacket(0).questions.size)
+ }
+
+ @Test
fun testProbeMultipleRecords() {
val replySender = MdnsReplySender("testiface", thread.looper, socket, buffer)
val prober = TestProber(thread.looper, replySender, cb)
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
index 4a39b93..0033b5a 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordRepositoryTest.kt
@@ -43,6 +43,7 @@
private const val TEST_SERVICE_ID_1 = 42
private const val TEST_SERVICE_ID_2 = 43
+private const val TEST_SERVICE_ID_3 = 44
private const val TEST_PORT = 12345
private const val TEST_SUBTYPE = "_subtype"
private val TEST_HOSTNAME = arrayOf("Android_000102030405060708090A0B0C0D0E0F", "local")
@@ -63,6 +64,12 @@
port = TEST_PORT
}
+private val TEST_SERVICE_3 = NsdServiceInfo().apply {
+ serviceType = "_TESTSERVICE._tcp"
+ serviceName = "MyTESTSERVICE"
+ port = TEST_PORT
+}
+
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
class MdnsRecordRepositoryTest {
@@ -124,6 +131,9 @@
assertFailsWith(NameConflictException::class) {
repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1, null /* subtype */)
}
+ assertFailsWith(NameConflictException::class) {
+ repository.addService(TEST_SERVICE_ID_3, TEST_SERVICE_3, null /* subtype */)
+ }
}
@Test
@@ -365,6 +375,27 @@
}
@Test
+ fun testGetReplyCaseInsensitive() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
+ val questionsCaseInSensitive =
+ listOf(MdnsPointerRecord(arrayOf("_TESTSERVICE", "_TCP", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ // TTL and data is empty for a question
+ 0L /* ttlMillis */,
+ null /* pointer */))
+ val queryCaseInsensitive = MdnsPacket(0 /* flags */, questionsCaseInSensitive,
+ listOf() /* answers */, listOf() /* authorityRecords */,
+ listOf() /* additionalRecords */)
+ val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
+ val replyCaseInsensitive = repository.getReply(queryCaseInsensitive, src)
+ assertNotNull(replyCaseInsensitive)
+ assertEquals(1, replyCaseInsensitive.answers.size)
+ assertEquals(7, replyCaseInsensitive.additionalAnswers.size)
+ }
+
+ @Test
fun testGetReply() {
doGetReplyTest(subtype = null)
}
@@ -490,6 +521,34 @@
}
@Test
+ fun testGetConflictingServicesCaseInsensitive() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
+
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MYTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port + 1,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MYOTHERTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */, 0L /* ttlMillis */,
+ listOf(TextEntry.fromString("somedifferent=entry"))),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ assertEquals(setOf(TEST_SERVICE_ID_1, TEST_SERVICE_ID_2),
+ repository.getConflictingServices(packet))
+ }
+
+ @Test
fun testGetConflictingServices_IdenticalService() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
@@ -505,7 +564,7 @@
0L /* receiptTimeMillis */, true /* cacheFlush */,
otherTtlMillis, 0 /* servicePriority */, 0 /* serviceWeight */,
TEST_SERVICE_1.port,
- TEST_HOSTNAME),
+ arrayOf("ANDROID_000102030405060708090A0B0C0D0E0F", "local")),
MdnsTextRecord(
arrayOf("MyOtherTestService", "_testservice", "_tcp", "local"),
0L /* receiptTimeMillis */, true /* cacheFlush */,
@@ -517,6 +576,35 @@
// Above records are identical to the actual registrations: no conflict
assertEquals(emptySet(), repository.getConflictingServices(packet))
}
+
+ @Test
+ fun testGetConflictingServicesCaseInsensitive_IdenticalService() {
+ val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
+ repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1, null /* subtype */)
+ repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_2, null /* subtype */)
+
+ val otherTtlMillis = 1234L
+ val packet = MdnsPacket(
+ 0 /* flags */,
+ emptyList() /* questions */,
+ listOf(
+ MdnsServiceRecord(
+ arrayOf("MYTESTSERVICE", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, 0 /* servicePriority */, 0 /* serviceWeight */,
+ TEST_SERVICE_1.port,
+ TEST_HOSTNAME),
+ MdnsTextRecord(
+ arrayOf("MyOtherTestService", "_TESTSERVICE", "_tcp", "local"),
+ 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ otherTtlMillis, emptyList()),
+ ) /* answers */,
+ emptyList() /* authorityRecords */,
+ emptyList() /* additionalRecords */)
+
+ // Above records are identical to the actual registrations: no conflict
+ assertEquals(emptySet(), repository.getConflictingServices(packet))
+ }
}
private fun MdnsRecordRepository.initWithService(
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt
new file mode 100644
index 0000000..8f819b7
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsRecordTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.mdns
+
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+class MdnsRecordTest {
+
+ @Test
+ fun testPointerRecordHasSubType() {
+ val ptrRecord1 = MdnsPointerRecord(
+ arrayOf("_testtype", "_sub", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ val ptrRecord2 = MdnsPointerRecord(
+ arrayOf("_testtype", "_SUB", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ assertTrue(ptrRecord1.hasSubtype())
+ assertTrue(ptrRecord2.hasSubtype())
+ }
+
+ @Test
+ fun testEqualsCaseInsensitive() {
+ val ptrRecord1 = MdnsPointerRecord(
+ arrayOf("_testtype", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testservice", "_testtype", "_tcp", "local")
+ )
+ val ptrRecord2 = MdnsPointerRecord(
+ arrayOf("_testType", "_tcp", "local"),
+ 0L /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 4500000 /* ttlMillis */,
+ arrayOf("testsErvice", "_testtype", "_Tcp", "local")
+ )
+ assertEquals(ptrRecord1, ptrRecord2)
+ assertEquals(ptrRecord1.hashCode(), ptrRecord2.hashCode())
+
+ val srvRecord1 = MdnsServiceRecord(
+ arrayOf("testservice", "_testtype", "_tcp", "local"),
+ 123 /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 2000 /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ 80 /* port */,
+ arrayOf("hostname")
+ )
+ val srvRecord2 = MdnsServiceRecord(
+ arrayOf("Testservice", "_testtype", "_tcp", "local"),
+ 123 /* receiptTimeMillis */,
+ false /* cacheFlush */,
+ 2000 /* ttlMillis */,
+ 0 /* servicePriority */,
+ 0 /* serviceWeight */,
+ 80 /* port */,
+ arrayOf("Hostname")
+ )
+ assertEquals(srvRecord1, srvRecord2)
+ assertEquals(srvRecord1.hashCode(), srvRecord2.hashCode())
+
+ val nsecRecord1 = MdnsNsecRecord(
+ arrayOf("hostname"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 2000L, /* ttlMillis */
+ arrayOf("hostname"),
+ intArrayOf(1, 2, 3) /* types */
+ )
+ val nsecRecord2 = MdnsNsecRecord(
+ arrayOf("HOSTNAME"),
+ 0L /* receiptTimeMillis */,
+ true /* cacheFlush */,
+ 2000L, /* ttlMillis */
+ arrayOf("HOSTNAME"),
+ intArrayOf(1, 2, 3) /* types */
+ )
+ assertEquals(nsecRecord1, nsecRecord2)
+ assertEquals(nsecRecord1.hashCode(), nsecRecord2.hashCode())
+ }
+
+ @Test
+ fun testLabelsAreSuffix() {
+ val labels1 = arrayOf("a", "b", "c")
+ val labels2 = arrayOf("B", "C")
+ val labels3 = arrayOf("b", "c")
+ val labels4 = arrayOf("b", "d")
+ assertTrue(MdnsRecord.labelsAreSuffix(labels2, labels1))
+ assertTrue(MdnsRecord.labelsAreSuffix(labels3, labels1))
+ assertFalse(MdnsRecord.labelsAreSuffix(labels4, labels1))
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
index b0a1f18..e16c448 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseDecoderTests.java
@@ -269,14 +269,9 @@
assertEquals("st=0", textStrings.get(6));
}
- @Test
- public void testDecodeIPv6AnswerPacket() throws IOException {
- MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
- assertNotNull(data6);
-
- responses = decode(decoder, data6);
- assertEquals(1, responses.size());
- MdnsResponse response = responses.valueAt(0);
+ private void verifyResponse(ArraySet<MdnsResponse> responseArraySet) {
+ assertEquals(1, responseArraySet.size());
+ MdnsResponse response = responseArraySet.valueAt(0);
assertTrue(response.isComplete());
MdnsInetAddressRecord inet6AddressRecord = response.getInet6AddressRecord();
@@ -290,6 +285,22 @@
}
@Test
+ public void testDecodeIPv6AnswerPacket() throws IOException {
+ MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, CAST_SERVICE_TYPE);
+ assertNotNull(data6);
+ verifyResponse(decode(decoder, data6));
+ }
+
+ @Test
+ public void testDecodeCaseInsensitiveMatch() throws IOException {
+ final String[] castServiceTypeUpperCase =
+ new String[] {"_GOOGLECAST", "_TCP", "LOCAL"};
+ MdnsResponseDecoder decoder = new MdnsResponseDecoder(mClock, castServiceTypeUpperCase);
+ assertNotNull(data6);
+ verifyResponse(decode(decoder, data6));
+ }
+
+ @Test
public void testIsComplete() {
MdnsResponse response = new MdnsResponse(responses.valueAt(0));
assertTrue(response.isComplete());
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
index 3f5e7a1..dc0e646 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsResponseTests.java
@@ -228,6 +228,12 @@
final MdnsResponse response = makeCompleteResponse(TEST_TTL_MS);
assertFalse(response.addPointerRecord(response.getPointerRecords().get(0)));
+ final String[] serviceName = new String[] { "MYSERVICE", "_TYPE", "_tcp", "local" };
+ final String[] serviceType = new String[] { "_TYPE", "_tcp", "local" };
+ MdnsPointerRecord pointerRecordCaseInsensitive =
+ new MdnsPointerRecord(serviceType, 0L /* receiptTimeMillis */,
+ false /* cacheFlush */, TEST_TTL_MS, serviceName);
+ assertFalse(response.addPointerRecord(pointerRecordCaseInsensitive));
assertFalse(response.addInet6AddressRecord(response.getInet6AddressRecord()));
assertFalse(response.addInet4AddressRecord(response.getInet4AddressRecord()));
assertFalse(response.setServiceRecord(response.getServiceRecord()));
@@ -270,4 +276,30 @@
// All records were replaced, not added
assertEquals(ttlZeroResponse.getRecords().size(), response.getRecords().size());
}
+
+ @Test
+ public void dropUnmatchedAddressRecords_caseInsensitive() {
+
+ final String[] hostname = new String[] { "MyHostname" };
+ final String[] upperCaseHostName = new String[] { "MYHOSTNAME" };
+ final String[] serviceName = new String[] { "MyService", "_type", "_tcp", "local" };
+ final String[] serviceType = new String[] { "_type", "_tcp", "local" };
+ final MdnsResponse response = new MdnsResponse(/* now= */ 0, serviceName, INTERFACE_INDEX,
+ mNetwork);
+ response.addPointerRecord(new MdnsPointerRecord(serviceType, 0L /* receiptTimeMillis */,
+ false /* cacheFlush */, TEST_TTL_MS, serviceName));
+ response.setServiceRecord(new MdnsServiceRecord(serviceName, 0L /* receiptTimeMillis */,
+ true /* cacheFlush */, TEST_TTL_MS, 0 /* servicePriority */,
+ 0 /* serviceWeight */, 0 /* servicePort */, hostname));
+ response.setTextRecord(new MdnsTextRecord(serviceName, 0L /* receiptTimeMillis */,
+ true /* cacheFlush */, TEST_TTL_MS, emptyList() /* entries */));
+ response.addInet4AddressRecord(new MdnsInetAddressRecord(
+ upperCaseHostName , 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ TEST_TTL_MS, parseNumericAddress("192.0.2.123")));
+ response.addInet6AddressRecord(new MdnsInetAddressRecord(
+ upperCaseHostName, 0L /* receiptTimeMillis */, true /* cacheFlush */,
+ TEST_TTL_MS, parseNumericAddress("2001:db8::123")));
+
+ assertFalse(response.dropUnmatchedAddressRecords());
+ }
}
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index bd59156..42d296b 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -90,6 +90,7 @@
private static final long TEST_TTL = 120000L;
private static final long TEST_ELAPSED_REALTIME = 123L;
+ private static final long TEST_TIMEOUT_MS = 10_000L;
@Mock
private MdnsServiceBrowserListener mockListenerOne;
@@ -1035,10 +1036,11 @@
final String otherInstance = "instance2";
final String ipV4Address = "192.0.2.0";
final String ipV6Address = "2001:db8::";
+ final String capitalizedRequestInstance = "Instance1";
final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
// Use different case in the options
- .setResolveInstanceName("Instance1").build();
+ .setResolveInstanceName(capitalizedRequestInstance).build();
client.startSendAndReceive(mockListenerOne, resolveOptions);
client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
@@ -1066,8 +1068,9 @@
Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
// mockListenerOne gets notified for the requested instance
- verify(mockListenerOne).onServiceNameDiscovered(matchServiceName(requestedInstance));
- verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+ verify(mockListenerOne).onServiceNameDiscovered(
+ matchServiceName(capitalizedRequestInstance));
+ verify(mockListenerOne).onServiceFound(matchServiceName(capitalizedRequestInstance));
// ...but does not get any callback for the other instance
verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
@@ -1078,8 +1081,9 @@
// mockListenerTwo gets notified for both though
final InOrder inOrder = inOrder(mockListenerTwo);
inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
- matchServiceName(requestedInstance));
- inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+ matchServiceName(capitalizedRequestInstance));
+ inOrder.verify(mockListenerTwo).onServiceFound(
+ matchServiceName(capitalizedRequestInstance));
inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
@@ -1169,7 +1173,7 @@
}
@Test
- public void testNotifyAllServicesRemoved() {
+ public void testNotifySocketDestroyed() throws Exception {
client = new MdnsServiceTypeClient(
SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog);
@@ -1178,12 +1182,21 @@
final String ipV4Address = "192.0.2.0";
final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
- // Use different case in the options
- .setResolveInstanceName("Instance1").build();
+ .setResolveInstanceName("instance1").build();
client.startSendAndReceive(mockListenerOne, resolveOptions);
+ // Ensure the first task is executed so it schedules a future task
+ currentThreadExecutor.getAndClearSubmittedFuture().get(
+ TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+ // Filing the second request cancels the first future
+ verify(expectedSendFutures[0]).cancel(true);
+
+ // Ensure it gets executed too
+ currentThreadExecutor.getAndClearSubmittedFuture().get(
+ TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
// Complete response from instanceName
client.processResponse(createResponse(
requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
@@ -1196,7 +1209,9 @@
Collections.emptyMap() /* textAttributes */, TEST_TTL),
INTERFACE_INDEX, mockNetwork);
- client.notifyAllServicesRemoved();
+ verify(expectedSendFutures[1], never()).cancel(true);
+ client.notifySocketDestroyed();
+ verify(expectedSendFutures[1]).cancel(true);
// mockListenerOne gets notified for the requested instance
final InOrder inOrder1 = inOrder(mockListenerOne);
@@ -1261,6 +1276,7 @@
private long lastScheduledDelayInMs;
private Runnable lastScheduledRunnable;
private Runnable lastSubmittedRunnable;
+ private Future<?> lastSubmittedFuture;
private int futureIndex;
FakeExecutor() {
@@ -1272,6 +1288,7 @@
public Future<?> submit(Runnable command) {
Future<?> future = super.submit(command);
lastSubmittedRunnable = command;
+ lastSubmittedFuture = future;
return future;
}
@@ -1303,6 +1320,12 @@
lastSubmittedRunnable = null;
return val;
}
+
+ Future<?> getAndClearSubmittedFuture() {
+ Future<?> val = lastSubmittedFuture;
+ lastSubmittedFuture = null;
+ return val;
+ }
}
private MdnsPacket createResponse(
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index 744ec84..4b87556 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -349,8 +349,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
- // Expect the socket destroy for tethered interface.
- testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
+ // There was still a tethered interface, but no callback should be sent once unregistered
+ testCallback3.expectedNoCallback();
}
private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(
@@ -528,7 +528,8 @@
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- testCallback.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
+ // No callback sent when unregistered
+ testCallback.expectedNoCallback();
verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, times(1)).unregisterTetheringEventCallback(any());
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
index 61c3a70..f705bcb 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/util/MdnsUtilsTest.kt
@@ -17,11 +17,14 @@
package com.android.server.connectivity.mdns.util
import android.os.Build
+import com.android.server.connectivity.mdns.util.MdnsUtils.equalsDnsLabelIgnoreDnsCase
import com.android.server.connectivity.mdns.util.MdnsUtils.equalsIgnoreDnsCase
+import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLabelsLowerCase
import com.android.server.connectivity.mdns.util.MdnsUtils.toDnsLowerCase
import com.android.server.connectivity.mdns.util.MdnsUtils.truncateServiceName
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -50,6 +53,12 @@
}
@Test
+ fun testToDnsLabelsLowerCase() {
+ assertArrayEquals(arrayOf("test", "tÉst", "ţést"),
+ toDnsLabelsLowerCase(arrayOf("TeSt", "TÉST", "ţést")))
+ }
+
+ @Test
fun testEqualsIgnoreDnsCase() {
assertTrue(equalsIgnoreDnsCase("TEST", "Test"))
assertTrue(equalsIgnoreDnsCase("TEST", "test"))
@@ -72,4 +81,25 @@
assertEquals(truncateServiceName("测试abcde", 7), "测试a")
assertEquals(truncateServiceName("测试abcde", 100), "测试abcde")
}
+
+ @Test
+ fun testEqualsLabelIgnoreDnsCase() {
+ assertTrue(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("Test"), arrayOf("test", "test")))
+ assertFalse(equalsDnsLabelIgnoreDnsCase(arrayOf("TEST", "Test"), arrayOf("test", "tést")))
+ }
+
+ @Test
+ fun testTypeEqualsOrIsSubtype() {
+ assertTrue(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_type", "_tcp", "local"),
+ arrayOf("_type", "_TCP", "local")))
+ assertTrue(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_type", "_tcp", "local"),
+ arrayOf("a", "_SUB", "_type", "_TCP", "local")))
+ assertFalse(MdnsUtils.typeEqualsOrIsSubtype(arrayOf("_sub", "_type", "_tcp", "local"),
+ arrayOf("_type", "_TCP", "local")))
+ assertFalse(MdnsUtils.typeEqualsOrIsSubtype(
+ arrayOf("a", "_other", "_type", "_tcp", "local"),
+ arrayOf("a", "_SUB", "_type", "_TCP", "local")))
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 99f6d63..b8b0289 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -1926,12 +1926,17 @@
// Templates w/o wifi network keys can query stats as usual.
assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+ // Templates for test network does not need to enforce location permission.
+ final NetworkTemplate templateTestIface1 = new NetworkTemplate.Builder(MATCH_TEST)
+ .setWifiNetworkKeys(Set.of(TEST_IFACE)).build();
+ assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
doReturn(true).when(mLocationPermissionChecker)
.checkCallersLocationPermission(any(), any(), anyInt(), anyBoolean(), any());
assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+ assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
}
/**