Merge "Translate v4 keepalive packet on clat started network to v6"
diff --git a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt b/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
index 0760e68..0885f4f 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/BidirectionalStreamTest.kt
@@ -27,6 +27,7 @@
import androidx.test.core.app.ApplicationProvider
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlin.test.assertEquals
import org.hamcrest.MatcherAssert
@@ -81,4 +82,113 @@
"Received byte count must be > 0", info.receivedByteCount, Matchers.greaterThan(0L))
assertEquals("h2", info.negotiatedProtocol)
}
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getHttpMethod() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val method = "GET"
+
+ builder.setHttpMethod(method)
+ stream = builder.build()
+ assertThat(stream!!.getHttpMethod()).isEqualTo(method)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_hasTrafficStatsTag() {
+ val builder = createBidirectionalStreamBuilder(URL)
+
+ builder.setTrafficStatsTag(10)
+ stream = builder.build()
+ assertThat(stream!!.hasTrafficStatsTag()).isTrue()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getTrafficStatsTag() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val trafficStatsTag = 10
+
+ builder.setTrafficStatsTag(trafficStatsTag)
+ stream = builder.build()
+ assertThat(stream!!.getTrafficStatsTag()).isEqualTo(trafficStatsTag)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_hasTrafficStatsUid() {
+ val builder = createBidirectionalStreamBuilder(URL)
+
+ builder.setTrafficStatsUid(10)
+ stream = builder.build()
+ assertThat(stream!!.hasTrafficStatsUid()).isTrue()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getTrafficStatsUid() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val trafficStatsUid = 10
+
+ builder.setTrafficStatsUid(trafficStatsUid)
+ stream = builder.build()
+ assertThat(stream!!.getTrafficStatsUid()).isEqualTo(trafficStatsUid)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getHeaders_asList() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val expectedHeaders = mapOf(
+ "Authorization" to "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
+ "Max-Forwards" to "10",
+ "X-Client-Data" to "random custom header content").entries.toList()
+
+ for (header in expectedHeaders) {
+ builder.addHeader(header.key, header.value)
+ }
+
+ stream = builder.build()
+ assertThat(stream!!.getHeaders().getAsList()).containsAtLeastElementsIn(expectedHeaders)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getHeaders_asMap() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val expectedHeaders = mapOf(
+ "Authorization" to listOf("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
+ "Max-Forwards" to listOf("10"),
+ "X-Client-Data" to listOf("random custom header content"))
+
+ for (header in expectedHeaders) {
+ builder.addHeader(header.key, header.value.get(0))
+ }
+
+ stream = builder.build()
+ assertThat(stream!!.getHeaders().getAsMap()).containsAtLeastEntriesIn(expectedHeaders)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_getPriority() {
+ val builder = createBidirectionalStreamBuilder(URL)
+ val priority = BidirectionalStream.STREAM_PRIORITY_LOW
+
+ builder.setPriority(priority)
+ stream = builder.build()
+ assertThat(stream!!.getPriority()).isEqualTo(priority)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testBidirectionalStream_isDelayRequestHeadersUntilFirstFlushEnabled() {
+ val builder = createBidirectionalStreamBuilder(URL)
+
+ builder.setDelayRequestHeadersUntilFirstFlushEnabled(true)
+ stream = builder.build()
+ assertThat(stream!!.isDelayRequestHeadersUntilFirstFlushEnabled()).isTrue()
+ }
+
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
index 07e7d45..3c4d134 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -363,6 +363,116 @@
.containsAtLeastElementsIn(expectedHeaders);
}
+ @Test
+ public void testUrlRequest_getHttpMethod() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final String method = "POST";
+
+ builder.setHttpMethod(method);
+ UrlRequest request = builder.build();
+ assertThat(request.getHttpMethod()).isEqualTo(method);
+ }
+
+ @Test
+ public void testUrlRequest_getHeaders_asList() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final List<Map.Entry<String, String>> expectedHeaders = Arrays.asList(
+ Map.entry("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
+ Map.entry("Max-Forwards", "10"),
+ Map.entry("X-Client-Data", "random custom header content"));
+
+ for (Map.Entry<String, String> header : expectedHeaders) {
+ builder.addHeader(header.getKey(), header.getValue());
+ }
+
+ UrlRequest request = builder.build();
+ assertThat(request.getHeaders().getAsList()).containsAtLeastElementsIn(expectedHeaders);
+ }
+
+ @Test
+ public void testUrlRequest_getHeaders_asMap() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final Map<String, List<String>> expectedHeaders = Map.of(
+ "Authorization", Arrays.asList("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
+ "Max-Forwards", Arrays.asList("10"),
+ "X-Client-Data", Arrays.asList("random custom header content"));
+
+ for (Map.Entry<String, List<String>> header : expectedHeaders.entrySet()) {
+ builder.addHeader(header.getKey(), header.getValue().get(0));
+ }
+
+ UrlRequest request = builder.build();
+ assertThat(request.getHeaders().getAsMap()).containsAtLeastEntriesIn(expectedHeaders);
+ }
+
+ @Test
+ public void testUrlRequest_isCacheDisabled() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final boolean isCacheDisabled = true;
+
+ builder.setCacheDisabled(isCacheDisabled);
+ UrlRequest request = builder.build();
+ assertThat(request.isCacheDisabled()).isEqualTo(isCacheDisabled);
+ }
+
+ @Test
+ public void testUrlRequest_isDirectExecutorAllowed() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final boolean isDirectExecutorAllowed = true;
+
+ builder.setDirectExecutorAllowed(isDirectExecutorAllowed);
+ UrlRequest request = builder.build();
+ assertThat(request.isDirectExecutorAllowed()).isEqualTo(isDirectExecutorAllowed);
+ }
+
+ @Test
+ public void testUrlRequest_getPriority() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final int priority = UrlRequest.REQUEST_PRIORITY_LOW;
+
+ builder.setPriority(priority);
+ UrlRequest request = builder.build();
+ assertThat(request.getPriority()).isEqualTo(priority);
+ }
+
+ @Test
+ public void testUrlRequest_hasTrafficStatsTag() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+
+ builder.setTrafficStatsTag(10);
+ UrlRequest request = builder.build();
+ assertThat(request.hasTrafficStatsTag()).isEqualTo(true);
+ }
+
+ @Test
+ public void testUrlRequest_getTrafficStatsTag() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final int trafficStatsTag = 10;
+
+ builder.setTrafficStatsTag(trafficStatsTag);
+ UrlRequest request = builder.build();
+ assertThat(request.getTrafficStatsTag()).isEqualTo(trafficStatsTag);
+ }
+
+ @Test
+ public void testUrlRequest_hasTrafficStatsUid() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+
+ builder.setTrafficStatsUid(10);
+ UrlRequest request = builder.build();
+ assertThat(request.hasTrafficStatsUid()).isEqualTo(true);
+ }
+
+ @Test
+ public void testUrlRequest_getTrafficStatsUid() throws Exception {
+ UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getSuccessUrl());
+ final int trafficStatsUid = 10;
+
+ builder.setTrafficStatsUid(trafficStatsUid);
+ UrlRequest request = builder.build();
+ assertThat(request.getTrafficStatsUid()).isEqualTo(trafficStatsUid);
+ }
+
private static List<Map.Entry<String, String>> extractEchoedHeaders(HeaderBlock headers) {
return headers.getAsList()
.stream()
diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
index 007bf23..9162546 100644
--- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
+++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java
@@ -25,17 +25,14 @@
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringTester.buildTcpPacket;
+import static android.net.TetheringTester.buildUdpPacket;
+import static android.net.TetheringTester.isAddressIpv4;
import static android.net.TetheringTester.isExpectedIcmpPacket;
import static android.net.TetheringTester.isExpectedTcpPacket;
import static android.net.TetheringTester.isExpectedUdpPacket;
-import static android.system.OsConstants.IPPROTO_IP;
-import static android.system.OsConstants.IPPROTO_IPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.HexDump.dumpHexString;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static com.android.net.module.util.NetworkStackConstants.TCPHDR_ACK;
import static com.android.net.module.util.NetworkStackConstants.TCPHDR_SYN;
@@ -67,11 +64,9 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.PacketBuilder;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.Ipv6Header;
import com.android.testutils.HandlerUtils;
@@ -124,29 +119,17 @@
(Inet4Address) parseNumericAddress("8.8.8.8");
protected static final Inet6Address REMOTE_IP6_ADDR =
(Inet6Address) parseNumericAddress("2002:db8:1::515:ca");
+ // The IPv6 network address translation of REMOTE_IP4_ADDR if pref64::/n is 64:ff9b::/96.
+ // For more information, see TetheringTester#PREF64_IPV4ONLY_ADDR, which assumes a prefix
+ // of 64:ff9b::/96.
protected static final Inet6Address REMOTE_NAT64_ADDR =
(Inet6Address) parseNumericAddress("64:ff9b::808:808");
- protected static final IpPrefix TEST_NAT64PREFIX = new IpPrefix("64:ff9b::/96");
- // IPv4 header definition.
- protected static final short ID = 27149;
- protected static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
- protected static final byte TIME_TO_LIVE = (byte) 0x40;
- protected static final byte TYPE_OF_SERVICE = 0;
-
- // IPv6 header definition.
- private static final short HOP_LIMIT = 0x40;
- // version=6, traffic class=0x0, flowlabel=0x0;
- private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
-
- // UDP and TCP header definition.
// LOCAL_PORT is used by public port and private port. Assume port 9876 has not been used yet
// before the testing that public port and private port are the same in the testing. Note that
// NAT port forwarding could be different between private port and public port.
protected static final short LOCAL_PORT = 9876;
protected static final short REMOTE_PORT = 433;
- private static final short WINDOW = (short) 0x2000;
- private static final short URGENT_POINTER = 0;
// Payload definition.
protected static final ByteBuffer EMPTY_PAYLOAD = ByteBuffer.wrap(new byte[0]);
@@ -649,77 +632,10 @@
final LinkProperties lp = new LinkProperties();
lp.setLinkAddresses(addresses);
lp.setDnsServers(dnses);
- lp.setNat64Prefix(TEST_NAT64PREFIX);
return runAsShell(MANAGE_TEST_NETWORKS, () -> initTestNetwork(mContext, lp, TIMEOUT_MS));
}
- private short getEthType(@NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp) {
- return isAddressIpv4(srcIp, dstIp) ? (short) ETHER_TYPE_IPV4 : (short) ETHER_TYPE_IPV6;
- }
-
- private int getIpProto(@NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp) {
- return isAddressIpv4(srcIp, dstIp) ? IPPROTO_IP : IPPROTO_IPV6;
- }
-
- @NonNull
- protected ByteBuffer buildUdpPacket(
- @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
- @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
- short srcPort, short dstPort, @Nullable final ByteBuffer payload)
- throws Exception {
- final int ipProto = getIpProto(srcIp, dstIp);
- final boolean hasEther = (srcMac != null && dstMac != null);
- final int payloadLen = (payload == null) ? 0 : payload.limit();
- final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_UDP,
- payloadLen);
- final PacketBuilder packetBuilder = new PacketBuilder(buffer);
-
- // [1] Ethernet header
- if (hasEther) {
- packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
- }
-
- // [2] IP header
- if (ipProto == IPPROTO_IP) {
- packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
- TIME_TO_LIVE, (byte) IPPROTO_UDP, (Inet4Address) srcIp, (Inet4Address) dstIp);
- } else {
- packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_UDP,
- HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
- }
-
- // [3] UDP header
- packetBuilder.writeUdpHeader(srcPort, dstPort);
-
- // [4] Payload
- if (payload != null) {
- buffer.put(payload);
- // in case data might be reused by caller, restore the position and
- // limit of bytebuffer.
- payload.clear();
- }
-
- return packetBuilder.finalizePacket();
- }
-
- @NonNull
- protected ByteBuffer buildUdpPacket(@NonNull final InetAddress srcIp,
- @NonNull final InetAddress dstIp, short srcPort, short dstPort,
- @Nullable final ByteBuffer payload) throws Exception {
- return buildUdpPacket(null /* srcMac */, null /* dstMac */, srcIp, dstIp, srcPort,
- dstPort, payload);
- }
-
- private boolean isAddressIpv4(@NonNull final InetAddress srcIp,
- @NonNull final InetAddress dstIp) {
- if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
- if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
-
- fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
- return false; // unreachable
- }
-
protected void sendDownloadPacketUdp(@NonNull final InetAddress srcIp,
@NonNull final InetAddress dstIp, @NonNull final TetheringTester tester,
boolean is6To4) throws Exception {
@@ -761,45 +677,6 @@
});
}
-
- @NonNull
- private ByteBuffer buildTcpPacket(
- @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
- @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
- short srcPort, short dstPort, final short seq, final short ack,
- final byte tcpFlags, @NonNull final ByteBuffer payload) throws Exception {
- final int ipProto = getIpProto(srcIp, dstIp);
- final boolean hasEther = (srcMac != null && dstMac != null);
- final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_TCP,
- payload.limit());
- final PacketBuilder packetBuilder = new PacketBuilder(buffer);
-
- // [1] Ethernet header
- if (hasEther) {
- packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
- }
-
- // [2] IP header
- if (ipProto == IPPROTO_IP) {
- packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
- TIME_TO_LIVE, (byte) IPPROTO_TCP, (Inet4Address) srcIp, (Inet4Address) dstIp);
- } else {
- packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_TCP,
- HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
- }
-
- // [3] TCP header
- packetBuilder.writeTcpHeader(srcPort, dstPort, seq, ack, tcpFlags, WINDOW, URGENT_POINTER);
-
- // [4] Payload
- buffer.put(payload);
- // in case data might be reused by caller, restore the position and
- // limit of bytebuffer.
- payload.clear();
-
- return packetBuilder.finalizePacket();
- }
-
protected void sendDownloadPacketTcp(@NonNull final InetAddress srcIp,
@NonNull final InetAddress dstIp, short seq, short ack, byte tcpFlags,
@NonNull final ByteBuffer payload, @NonNull final TetheringTester tester,
diff --git a/Tethering/tests/integration/base/android/net/TetheringTester.java b/Tethering/tests/integration/base/android/net/TetheringTester.java
index 1c0803e..3f3768e 100644
--- a/Tethering/tests/integration/base/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/base/android/net/TetheringTester.java
@@ -16,17 +16,27 @@
package android.net;
+import static android.net.DnsResolver.CLASS_IN;
+import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.InetAddresses.parseNumericAddress;
+import static android.system.OsConstants.ICMP_ECHO;
+import static android.system.OsConstants.ICMP_ECHOREPLY;
import static android.system.OsConstants.IPPROTO_ICMP;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_IP;
+import static android.system.OsConstants.IPPROTO_IPV6;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.DnsPacket.ANSECTION;
import static com.android.net.module.util.DnsPacket.ARSECTION;
+import static com.android.net.module.util.DnsPacket.DnsHeader;
+import static com.android.net.module.util.DnsPacket.DnsRecord;
import static com.android.net.module.util.DnsPacket.NSSECTION;
import static com.android.net.module.util.DnsPacket.QDSECTION;
import static com.android.net.module.util.HexDump.dumpHexString;
+import static com.android.net.module.util.IpUtils.icmpChecksum;
+import static com.android.net.module.util.IpUtils.ipChecksum;
import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
@@ -38,6 +48,10 @@
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.net.module.util.NetworkStackConstants.ICMP_CHECKSUM_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
@@ -58,6 +72,7 @@
import com.android.net.module.util.DnsPacket;
import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.PacketBuilder;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv4Header;
@@ -101,6 +116,44 @@
DhcpPacket.DHCP_LEASE_TIME,
};
private static final InetAddress LINK_LOCAL = parseNumericAddress("fe80::1");
+ // IPv4 header definition.
+ protected static final short ID = 27149;
+ protected static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
+ protected static final byte TIME_TO_LIVE = (byte) 0x40;
+ protected static final byte TYPE_OF_SERVICE = 0;
+
+ // IPv6 header definition.
+ private static final short HOP_LIMIT = 0x40;
+ // version=6, traffic class=0x0, flowlabel=0x0;
+ private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
+
+ // UDP and TCP header definition.
+ private static final short WINDOW = (short) 0x2000;
+ private static final short URGENT_POINTER = 0;
+
+ // ICMP definition.
+ private static final short ICMPECHO_CODE = 0x0;
+
+ // Prefix64 discovery definition. See RFC 7050 section 8.
+ // Note that the AAAA response Pref64::WKAs consisting of Pref64::/n and WKA.
+ // Use 64:ff9b::/96 as Pref64::/n and WKA 192.0.0.17{0|1} here.
+ //
+ // Host DNS64 server
+ // | |
+ // | "AAAA" query for "ipv4only.arpa." |
+ // |----------------------------------------------->|
+ // | |
+ // | "AAAA" response with: |
+ // | "64:ff9b::192.0.0.170" |
+ // |<-----------------------------------------------|
+ //
+ private static final String PREF64_IPV4ONLY_HOSTNAME = "ipv4only.arpa";
+ private static final InetAddress PREF64_IPV4ONLY_ADDR = parseNumericAddress(
+ "64:ff9b::192.0.0.170");
+
+ // DNS header definition.
+ private static final short FLAG = (short) 0x8100; // qr, ra
+ private static final short TTL = (short) 0;
public static final String DHCP_HOSTNAME = "testhostname";
@@ -462,6 +515,11 @@
super(data);
}
+ TestDnsPacket(@NonNull DnsHeader header, @Nullable ArrayList<DnsRecord> qd,
+ @Nullable ArrayList<DnsRecord> an) {
+ super(header, qd, an);
+ }
+
@Nullable
public static TestDnsPacket getTestDnsPacket(final ByteBuffer buf) {
try {
@@ -628,6 +686,190 @@
return false;
}
+ @NonNull
+ public static ByteBuffer buildUdpPacket(
+ @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+ @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
+ short srcPort, short dstPort, @Nullable final ByteBuffer payload)
+ throws Exception {
+ final int ipProto = getIpProto(srcIp, dstIp);
+ final boolean hasEther = (srcMac != null && dstMac != null);
+ final int payloadLen = (payload == null) ? 0 : payload.limit();
+ final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_UDP,
+ payloadLen);
+ final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+ // [1] Ethernet header
+ if (hasEther) {
+ packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
+ }
+
+ // [2] IP header
+ if (ipProto == IPPROTO_IP) {
+ packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
+ TIME_TO_LIVE, (byte) IPPROTO_UDP, (Inet4Address) srcIp, (Inet4Address) dstIp);
+ } else {
+ packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_UDP,
+ HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
+ }
+
+ // [3] UDP header
+ packetBuilder.writeUdpHeader(srcPort, dstPort);
+
+ // [4] Payload
+ if (payload != null) {
+ buffer.put(payload);
+ // in case data might be reused by caller, restore the position and
+ // limit of bytebuffer.
+ payload.clear();
+ }
+
+ return packetBuilder.finalizePacket();
+ }
+
+ @NonNull
+ public static ByteBuffer buildUdpPacket(@NonNull final InetAddress srcIp,
+ @NonNull final InetAddress dstIp, short srcPort, short dstPort,
+ @Nullable final ByteBuffer payload) throws Exception {
+ return buildUdpPacket(null /* srcMac */, null /* dstMac */, srcIp, dstIp, srcPort,
+ dstPort, payload);
+ }
+
+ @NonNull
+ public static ByteBuffer buildTcpPacket(
+ @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+ @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
+ short srcPort, short dstPort, final short seq, final short ack,
+ final byte tcpFlags, @NonNull final ByteBuffer payload) throws Exception {
+ final int ipProto = getIpProto(srcIp, dstIp);
+ final boolean hasEther = (srcMac != null && dstMac != null);
+ final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_TCP,
+ payload.limit());
+ final PacketBuilder packetBuilder = new PacketBuilder(buffer);
+
+ // [1] Ethernet header
+ if (hasEther) {
+ packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
+ }
+
+ // [2] IP header
+ if (ipProto == IPPROTO_IP) {
+ packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
+ TIME_TO_LIVE, (byte) IPPROTO_TCP, (Inet4Address) srcIp, (Inet4Address) dstIp);
+ } else {
+ packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_TCP,
+ HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
+ }
+
+ // [3] TCP header
+ packetBuilder.writeTcpHeader(srcPort, dstPort, seq, ack, tcpFlags, WINDOW, URGENT_POINTER);
+
+ // [4] Payload
+ buffer.put(payload);
+ // in case data might be reused by caller, restore the position and
+ // limit of bytebuffer.
+ payload.clear();
+
+ return packetBuilder.finalizePacket();
+ }
+
+ // PacketBuilder doesn't support IPv4 ICMP packet. It may need to refactor PacketBuilder first
+ // because ICMP is a specific layer 3 protocol for PacketBuilder which expects packets always
+ // have layer 3 (IP) and layer 4 (TCP, UDP) for now. Since we don't use IPv4 ICMP packet too
+ // much in this test, we just write a ICMP packet builder here.
+ @NonNull
+ public static ByteBuffer buildIcmpEchoPacketV4(
+ @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
+ @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp,
+ int type, short id, short seq) throws Exception {
+ if (type != ICMP_ECHO && type != ICMP_ECHOREPLY) {
+ fail("Unsupported ICMP type: " + type);
+ }
+
+ // Build ICMP echo id and seq fields as payload. Ignore the data field.
+ final ByteBuffer payload = ByteBuffer.allocate(4);
+ payload.putShort(id);
+ payload.putShort(seq);
+ payload.rewind();
+
+ final boolean hasEther = (srcMac != null && dstMac != null);
+ final int etherHeaderLen = hasEther ? Struct.getSize(EthernetHeader.class) : 0;
+ final int ipv4HeaderLen = Struct.getSize(Ipv4Header.class);
+ final int Icmpv4HeaderLen = Struct.getSize(Icmpv4Header.class);
+ final int payloadLen = payload.limit();
+ final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv4HeaderLen
+ + Icmpv4HeaderLen + payloadLen);
+
+ // [1] Ethernet header
+ if (hasEther) {
+ final EthernetHeader ethHeader = new EthernetHeader(dstMac, srcMac, ETHER_TYPE_IPV4);
+ ethHeader.writeToByteBuffer(packet);
+ }
+
+ // [2] IP header
+ final Ipv4Header ipv4Header = new Ipv4Header(TYPE_OF_SERVICE,
+ (short) 0 /* totalLength, calculate later */, ID,
+ FLAGS_AND_FRAGMENT_OFFSET, TIME_TO_LIVE, (byte) IPPROTO_ICMP,
+ (short) 0 /* checksum, calculate later */, srcIp, dstIp);
+ ipv4Header.writeToByteBuffer(packet);
+
+ // [3] ICMP header
+ final Icmpv4Header icmpv4Header = new Icmpv4Header((byte) type, ICMPECHO_CODE,
+ (short) 0 /* checksum, calculate later */);
+ icmpv4Header.writeToByteBuffer(packet);
+
+ // [4] Payload
+ packet.put(payload);
+ packet.flip();
+
+ // [5] Finalize packet
+ // Used for updating IP header fields. If there is Ehternet header, IPv4 header offset
+ // in buffer equals ethernet header length because IPv4 header is located next to ethernet
+ // header. Otherwise, IPv4 header offset is 0.
+ final int ipv4HeaderOffset = hasEther ? etherHeaderLen : 0;
+
+ // Populate the IPv4 totalLength field.
+ packet.putShort(ipv4HeaderOffset + IPV4_LENGTH_OFFSET,
+ (short) (ipv4HeaderLen + Icmpv4HeaderLen + payloadLen));
+
+ // Populate the IPv4 header checksum field.
+ packet.putShort(ipv4HeaderOffset + IPV4_CHECKSUM_OFFSET,
+ ipChecksum(packet, ipv4HeaderOffset /* headerOffset */));
+
+ // Populate the ICMP checksum field.
+ packet.putShort(ipv4HeaderOffset + IPV4_HEADER_MIN_LEN + ICMP_CHECKSUM_OFFSET,
+ icmpChecksum(packet, ipv4HeaderOffset + IPV4_HEADER_MIN_LEN,
+ Icmpv4HeaderLen + payloadLen));
+ return packet;
+ }
+
+ @NonNull
+ public static ByteBuffer buildIcmpEchoPacketV4(@NonNull final Inet4Address srcIp,
+ @NonNull final Inet4Address dstIp, int type, short id, short seq)
+ throws Exception {
+ return buildIcmpEchoPacketV4(null /* srcMac */, null /* dstMac */, srcIp, dstIp,
+ type, id, seq);
+ }
+
+ private static short getEthType(@NonNull final InetAddress srcIp,
+ @NonNull final InetAddress dstIp) {
+ return isAddressIpv4(srcIp, dstIp) ? (short) ETHER_TYPE_IPV4 : (short) ETHER_TYPE_IPV6;
+ }
+
+ private static int getIpProto(@NonNull final InetAddress srcIp,
+ @NonNull final InetAddress dstIp) {
+ return isAddressIpv4(srcIp, dstIp) ? IPPROTO_IP : IPPROTO_IPV6;
+ }
+
+ public static boolean isAddressIpv4(@NonNull final InetAddress srcIp,
+ @NonNull final InetAddress dstIp) {
+ if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
+ if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
+
+ fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
+ return false; // unreachable
+ }
+
public void sendUploadPacket(ByteBuffer packet) throws Exception {
mDownstreamReader.sendResponse(packet);
}
@@ -650,10 +892,85 @@
return null;
}
+ @NonNull
+ private ByteBuffer buildUdpDnsPrefix64ReplyPacket(int dnsId, @NonNull final Inet6Address srcIp,
+ @NonNull final Inet6Address dstIp, short srcPort, short dstPort) throws Exception {
+ // [1] Build prefix64 DNS message.
+ final ArrayList<DnsRecord> qlist = new ArrayList<>();
+ // Fill QD section.
+ qlist.add(DnsRecord.makeQuestion(PREF64_IPV4ONLY_HOSTNAME, TYPE_AAAA, CLASS_IN));
+ final ArrayList<DnsRecord> alist = new ArrayList<>();
+ // Fill AN sections.
+ alist.add(DnsRecord.makeAOrAAAARecord(ANSECTION, PREF64_IPV4ONLY_HOSTNAME, CLASS_IN, TTL,
+ PREF64_IPV4ONLY_ADDR));
+ final TestDnsPacket dns = new TestDnsPacket(
+ new DnsHeader(dnsId, FLAG, qlist.size(), alist.size()), qlist, alist);
+
+ // [2] Build IPv6 UDP DNS packet.
+ return buildUdpPacket(srcIp, dstIp, srcPort, dstPort, ByteBuffer.wrap(dns.getBytes()));
+ }
+
+ private void maybeReplyUdpDnsPrefix64Discovery(@NonNull byte[] packet) {
+ final ByteBuffer buf = ByteBuffer.wrap(packet);
+
+ // [1] Parse the prefix64 discovery DNS query for hostname ipv4only.arpa.
+ // Parse IPv6 and UDP header.
+ Ipv6Header ipv6Header = null;
+ try {
+ ipv6Header = Struct.parse(Ipv6Header.class, buf);
+ if (ipv6Header == null || ipv6Header.nextHeader != IPPROTO_UDP) return;
+ } catch (Exception e) {
+ // Parsing packet fail means it is not IPv6 UDP packet.
+ return;
+ }
+ final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
+
+ // Parse DNS message.
+ final TestDnsPacket pref64Query = TestDnsPacket.getTestDnsPacket(buf);
+ if (pref64Query == null) return;
+ if (pref64Query.getHeader().isResponse()) return;
+ if (pref64Query.getQDCount() != 1) return;
+ if (pref64Query.getANCount() != 0) return;
+ if (pref64Query.getNSCount() != 0) return;
+ if (pref64Query.getARCount() != 0) return;
+
+ final List<DnsRecord> qdRecordList = pref64Query.getRecordList(QDSECTION);
+ if (qdRecordList.size() != 1) return;
+ if (!qdRecordList.get(0).dName.equals(PREF64_IPV4ONLY_HOSTNAME)) return;
+
+ // [2] Build prefix64 DNS discovery reply from received query.
+ // DNS response transaction id must be copied from DNS query. Used by the requester
+ // to match up replies to outstanding queries. See RFC 1035 section 4.1.1. Also reverse
+ // the source/destination address/port of query packet for building reply packet.
+ final ByteBuffer replyPacket;
+ try {
+ replyPacket = buildUdpDnsPrefix64ReplyPacket(pref64Query.getHeader().getId(),
+ ipv6Header.dstIp /* srcIp */, ipv6Header.srcIp /* dstIp */,
+ (short) udpHeader.dstPort /* srcPort */,
+ (short) udpHeader.srcPort /* dstPort */);
+ } catch (Exception e) {
+ fail("Failed to build prefix64 discovery reply for " + ipv6Header.srcIp + ": " + e);
+ return;
+ }
+
+ Log.d(TAG, "Sending prefix64 discovery reply");
+ try {
+ sendDownloadPacket(replyPacket);
+ } catch (Exception e) {
+ fail("Failed to reply prefix64 discovery for " + ipv6Header.srcIp + ": " + e);
+ }
+ }
+
private byte[] getUploadPacket(Predicate<byte[]> filter) {
assertNotNull("Can't deal with upstream interface in local only mode", mUpstreamReader);
- return mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS, filter);
+ byte[] packet;
+ while ((packet = mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) {
+ if (filter.test(packet)) return packet;
+
+ maybeReplyUdpDnsPrefix64Discovery(packet);
+ }
+ return null;
}
private @NonNull byte[] verifyPacketNotNull(String message, @Nullable byte[] packet) {
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 5d57aa5..eed308c 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -20,23 +20,17 @@
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringTester.TestDnsPacket;
+import static android.net.TetheringTester.buildIcmpEchoPacketV4;
+import static android.net.TetheringTester.buildUdpPacket;
import static android.net.TetheringTester.isExpectedIcmpPacket;
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
import static android.system.OsConstants.ICMP_ECHO;
import static android.system.OsConstants.ICMP_ECHOREPLY;
-import static android.system.OsConstants.IPPROTO_ICMP;
import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
import static com.android.net.module.util.HexDump.dumpHexString;
-import static com.android.net.module.util.IpUtils.icmpChecksum;
-import static com.android.net.module.util.IpUtils.ipChecksum;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static com.android.net.module.util.NetworkStackConstants.ICMP_CHECKSUM_OFFSET;
-import static com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET;
-import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
-import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -53,14 +47,11 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.Ipv6Utils;
import com.android.net.module.util.Struct;
-import com.android.net.module.util.structs.EthernetHeader;
-import com.android.net.module.util.structs.Icmpv4Header;
import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.UdpHeader;
import com.android.testutils.DevSdkIgnoreRule;
@@ -96,7 +87,6 @@
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
private static final short DNS_PORT = 53;
- private static final short ICMPECHO_CODE = 0x0;
private static final short ICMPECHO_ID = 0x0;
private static final short ICMPECHO_SEQ = 0x0;
@@ -564,85 +554,6 @@
runClatUdpTest();
}
- // PacketBuilder doesn't support IPv4 ICMP packet. It may need to refactor PacketBuilder first
- // because ICMP is a specific layer 3 protocol for PacketBuilder which expects packets always
- // have layer 3 (IP) and layer 4 (TCP, UDP) for now. Since we don't use IPv4 ICMP packet too
- // much in this test, we just write a ICMP packet builder here.
- // TODO: move ICMPv4 packet build function to common utilis.
- @NonNull
- private ByteBuffer buildIcmpEchoPacketV4(
- @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
- @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp,
- int type, short id, short seq) throws Exception {
- if (type != ICMP_ECHO && type != ICMP_ECHOREPLY) {
- fail("Unsupported ICMP type: " + type);
- }
-
- // Build ICMP echo id and seq fields as payload. Ignore the data field.
- final ByteBuffer payload = ByteBuffer.allocate(4);
- payload.putShort(id);
- payload.putShort(seq);
- payload.rewind();
-
- final boolean hasEther = (srcMac != null && dstMac != null);
- final int etherHeaderLen = hasEther ? Struct.getSize(EthernetHeader.class) : 0;
- final int ipv4HeaderLen = Struct.getSize(Ipv4Header.class);
- final int Icmpv4HeaderLen = Struct.getSize(Icmpv4Header.class);
- final int payloadLen = payload.limit();
- final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv4HeaderLen
- + Icmpv4HeaderLen + payloadLen);
-
- // [1] Ethernet header
- if (hasEther) {
- final EthernetHeader ethHeader = new EthernetHeader(dstMac, srcMac, ETHER_TYPE_IPV4);
- ethHeader.writeToByteBuffer(packet);
- }
-
- // [2] IP header
- final Ipv4Header ipv4Header = new Ipv4Header(TYPE_OF_SERVICE,
- (short) 0 /* totalLength, calculate later */, ID,
- FLAGS_AND_FRAGMENT_OFFSET, TIME_TO_LIVE, (byte) IPPROTO_ICMP,
- (short) 0 /* checksum, calculate later */, srcIp, dstIp);
- ipv4Header.writeToByteBuffer(packet);
-
- // [3] ICMP header
- final Icmpv4Header icmpv4Header = new Icmpv4Header((byte) type, ICMPECHO_CODE,
- (short) 0 /* checksum, calculate later */);
- icmpv4Header.writeToByteBuffer(packet);
-
- // [4] Payload
- packet.put(payload);
- packet.flip();
-
- // [5] Finalize packet
- // Used for updating IP header fields. If there is Ehternet header, IPv4 header offset
- // in buffer equals ethernet header length because IPv4 header is located next to ethernet
- // header. Otherwise, IPv4 header offset is 0.
- final int ipv4HeaderOffset = hasEther ? etherHeaderLen : 0;
-
- // Populate the IPv4 totalLength field.
- packet.putShort(ipv4HeaderOffset + IPV4_LENGTH_OFFSET,
- (short) (ipv4HeaderLen + Icmpv4HeaderLen + payloadLen));
-
- // Populate the IPv4 header checksum field.
- packet.putShort(ipv4HeaderOffset + IPV4_CHECKSUM_OFFSET,
- ipChecksum(packet, ipv4HeaderOffset /* headerOffset */));
-
- // Populate the ICMP checksum field.
- packet.putShort(ipv4HeaderOffset + IPV4_HEADER_MIN_LEN + ICMP_CHECKSUM_OFFSET,
- icmpChecksum(packet, ipv4HeaderOffset + IPV4_HEADER_MIN_LEN,
- Icmpv4HeaderLen + payloadLen));
- return packet;
- }
-
- @NonNull
- private ByteBuffer buildIcmpEchoPacketV4(@NonNull final Inet4Address srcIp,
- @NonNull final Inet4Address dstIp, int type, short id, short seq)
- throws Exception {
- return buildIcmpEchoPacketV4(null /* srcMac */, null /* dstMac */, srcIp, dstIp,
- type, id, seq);
- }
-
@Test
public void testIcmpv4Echo() throws Exception {
final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
index d538368..80c315a 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
@@ -29,16 +29,16 @@
namespace bpf {
namespace internal {
-void NetworkTracePoller::SchedulePolling() {
- // Schedules another run of ourselves to recursively poll periodically.
- mTaskRunner->PostDelayedTask(
- [this]() {
- mMutex.lock();
- SchedulePolling();
- ConsumeAllLocked();
- mMutex.unlock();
- },
- mPollMs);
+void NetworkTracePoller::PollAndSchedule(perfetto::base::TaskRunner* runner,
+ uint32_t poll_ms) {
+ // Always schedule another run of ourselves to recursively poll periodically.
+ // The task runner is sequential so these can't run on top of each other.
+ runner->PostDelayedTask([=]() { PollAndSchedule(runner, poll_ms); }, poll_ms);
+
+ if (mMutex.try_lock()) {
+ ConsumeAllLocked();
+ mMutex.unlock();
+ }
}
bool NetworkTracePoller::Start(uint32_t pollMs) {
@@ -81,7 +81,7 @@
// Start a task runner to run ConsumeAll every mPollMs milliseconds.
mTaskRunner = perfetto::Platform::GetDefaultPlatform()->CreateTaskRunner({});
mPollMs = pollMs;
- SchedulePolling();
+ PollAndSchedule(mTaskRunner.get(), mPollMs);
mSessionCount++;
return true;
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTracePoller.h b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTracePoller.h
index adde51e..8433934 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTracePoller.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/NetworkTracePoller.h
@@ -53,7 +53,12 @@
bool ConsumeAll() EXCLUDES(mMutex);
private:
- void SchedulePolling() REQUIRES(mMutex);
+ // Poll the ring buffer for new data and schedule another run of ourselves
+ // after poll_ms (essentially polling periodically until stopped). This takes
+ // in the runner and poll duration to prevent a hard requirement on the lock
+ // and thus a deadlock while resetting the TaskRunner. The runner pointer is
+ // always valid within tasks run by that runner.
+ void PollAndSchedule(perfetto::base::TaskRunner* runner, uint32_t poll_ms);
bool ConsumeAllLocked() REQUIRES(mMutex);
std::mutex mMutex;
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 25aa693..754a60b 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1317,10 +1317,9 @@
final NsdServiceInfo info = buildNsdServiceInfoFromMdnsEvent(event, code);
// Errors are already logged if null
if (info == null) return false;
- if (DBG) {
- Log.d(TAG, String.format("MdnsDiscoveryManager event code=%s transactionId=%d",
- NsdManager.nameOf(code), transactionId));
- }
+ mServiceLogs.log(String.format(
+ "MdnsDiscoveryManager event code=%s transactionId=%d",
+ NsdManager.nameOf(code), transactionId));
switch (code) {
case NsdManager.SERVICE_FOUND:
clientInfo.onServiceFound(clientId, info);
@@ -1722,6 +1721,7 @@
private class AdvertiserCallback implements MdnsAdvertiser.AdvertiserCallback {
@Override
public void onRegisterServiceSucceeded(int serviceId, NsdServiceInfo registeredInfo) {
+ mServiceLogs.log("onRegisterServiceSucceeded: serviceId " + serviceId);
final ClientInfo clientInfo = getClientInfoOrLog(serviceId);
if (clientInfo == null) return;
diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
index 2d5bb00..bd4ec20 100644
--- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
+++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.Network;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -70,8 +69,8 @@
private final List<String> subtypes;
private final boolean expectUnicastResponse;
private final int transactionId;
- @Nullable
- private final Network network;
+ @NonNull
+ private final SocketKey socketKey;
private final boolean sendDiscoveryQueries;
@NonNull
private final List<MdnsResponse> servicesToResolve;
@@ -86,7 +85,7 @@
@NonNull Collection<String> subtypes,
boolean expectUnicastResponse,
int transactionId,
- @Nullable Network network,
+ @NonNull SocketKey socketKey,
boolean onlyUseIpv6OnIpv6OnlyNetworks,
boolean sendDiscoveryQueries,
@NonNull Collection<MdnsResponse> servicesToResolve,
@@ -97,7 +96,7 @@
this.subtypes = new ArrayList<>(subtypes);
this.expectUnicastResponse = expectUnicastResponse;
this.transactionId = transactionId;
- this.network = network;
+ this.socketKey = socketKey;
this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
this.sendDiscoveryQueries = sendDiscoveryQueries;
this.servicesToResolve = new ArrayList<>(servicesToResolve);
@@ -216,7 +215,7 @@
if (expectUnicastResponse) {
if (requestSender instanceof MdnsMultinetworkSocketClient) {
((MdnsMultinetworkSocketClient) requestSender).sendPacketRequestingUnicastResponse(
- packet, network, onlyUseIpv6OnIpv6OnlyNetworks);
+ packet, socketKey, onlyUseIpv6OnIpv6OnlyNetworks);
} else {
requestSender.sendPacketRequestingUnicastResponse(
packet, onlyUseIpv6OnIpv6OnlyNetworks);
@@ -225,7 +224,7 @@
if (requestSender instanceof MdnsMultinetworkSocketClient) {
((MdnsMultinetworkSocketClient) requestSender)
.sendPacketRequestingMulticastResponse(
- packet, network, onlyUseIpv6OnIpv6OnlyNetworks);
+ packet, socketKey, onlyUseIpv6OnIpv6OnlyNetworks);
} else {
requestSender.sendPacketRequestingMulticastResponse(
packet, onlyUseIpv6OnIpv6OnlyNetworks);
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
index f0e1717..ce5f540 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsConstants.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.mdns;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
import static java.nio.charset.StandardCharsets.UTF_8;
import com.android.internal.annotations.VisibleForTesting;
@@ -25,7 +27,7 @@
import java.nio.charset.Charset;
/** mDNS-related constants. */
-@VisibleForTesting
+@VisibleForTesting(visibility = PACKAGE)
public final class MdnsConstants {
public static final int MDNS_PORT = 5353;
// Flags word format is:
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index afad3b7..05b1dcf 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -194,7 +194,7 @@
}
@Override
- public void onAllSocketsDestroyed(@NonNull SocketKey socketKey) {
+ public void onSocketDestroyed(@NonNull SocketKey socketKey) {
ensureRunningOnHandlerThread(handler);
final MdnsServiceTypeClient serviceTypeClient =
perSocketServiceTypeClients.get(serviceType, socketKey);
@@ -254,8 +254,7 @@
private void handleOnResponseReceived(@NonNull MdnsPacket packet,
@NonNull SocketKey socketKey) {
for (MdnsServiceTypeClient serviceTypeClient : getMdnsServiceTypeClient(socketKey)) {
- serviceTypeClient.processResponse(
- packet, socketKey.getInterfaceIndex(), socketKey.getNetwork());
+ serviceTypeClient.processResponse(packet, socketKey);
}
}
@@ -285,9 +284,11 @@
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@NonNull SocketKey socketKey) {
sharedLog.log("createServiceTypeClient for type:" + serviceType + " " + socketKey);
+ final String tag = serviceType + "-" + socketKey.getNetwork()
+ + "/" + socketKey.getInterfaceIndex();
return new MdnsServiceTypeClient(
serviceType, socketClient,
executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey,
- sharedLog.forSubComponent(serviceType + "-" + socketKey));
+ sharedLog.forSubComponent(tag));
}
}
\ No newline at end of file
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 1253444..d1fa57c 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -64,7 +64,7 @@
@NonNull
private final SocketCreationCallback mSocketCreationCallback;
@NonNull
- private final ArrayMap<MdnsInterfaceSocket, SocketKey> mActiveNetworkSockets =
+ private final ArrayMap<MdnsInterfaceSocket, SocketKey> mActiveSockets =
new ArrayMap<>();
InterfaceSocketCallback(SocketCreationCallback socketCreationCallback) {
@@ -83,7 +83,7 @@
mSocketPacketHandlers.put(socket, handler);
}
socket.addPacketHandler(handler);
- mActiveNetworkSockets.put(socket, socketKey);
+ mActiveSockets.put(socket, socketKey);
mSocketCreationCallback.onSocketCreated(socketKey);
}
@@ -95,16 +95,16 @@
}
private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
- final SocketKey socketKey = mActiveNetworkSockets.remove(socket);
- if (!isAnySocketActive(socketKey)) {
- mSocketCreationCallback.onAllSocketsDestroyed(socketKey);
+ final SocketKey socketKey = mActiveSockets.remove(socket);
+ if (!isSocketActive(socket)) {
+ mSocketCreationCallback.onSocketDestroyed(socketKey);
}
}
void onNetworkUnrequested() {
- for (int i = mActiveNetworkSockets.size() - 1; i >= 0; i--) {
+ for (int i = mActiveSockets.size() - 1; i >= 0; i--) {
// Iterate from the end so the socket can be removed
- final MdnsInterfaceSocket socket = mActiveNetworkSockets.keyAt(i);
+ final MdnsInterfaceSocket socket = mActiveSockets.keyAt(i);
notifySocketDestroyed(socket);
maybeCleanupPacketHandler(socket);
}
@@ -114,17 +114,7 @@
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(@NonNull SocketKey socketKey) {
- for (int i = 0; i < mRequestedNetworks.size(); i++) {
- final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
- if (isc.mActiveNetworkSockets.containsValue(socketKey)) {
+ if (isc.mActiveSockets.containsKey(socket)) {
return true;
}
}
@@ -135,7 +125,7 @@
final ArrayMap<MdnsInterfaceSocket, SocketKey> sockets = new ArrayMap<>();
for (int i = 0; i < mRequestedNetworks.size(); i++) {
final InterfaceSocketCallback isc = mRequestedNetworks.valueAt(i);
- sockets.putAll(isc.mActiveNetworkSockets);
+ sockets.putAll(isc.mActiveSockets);
}
return sockets;
}
@@ -213,25 +203,22 @@
return true;
}
- private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork,
+ private void sendMdnsPacket(@NonNull DatagramPacket packet, @NonNull SocketKey targetSocketKey,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet6Address;
final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet4Address;
final ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets = getActiveSockets();
- boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlyNetworks(
- activeSockets, targetNetwork);
+ boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlySockets(
+ activeSockets, targetSocketKey);
for (int i = 0; i < activeSockets.size(); i++) {
final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
- final Network network = activeSockets.valueAt(i).getNetwork();
+ final SocketKey socketKey = activeSockets.valueAt(i);
// Check ip capability and network before sending packet
if (((isIpv6 && socket.hasJoinedIpv6() && shouldQueryIpv6)
|| (isIpv4 && socket.hasJoinedIpv4()))
- // Contrary to MdnsUtils.isNetworkMatched, only send packets targeting
- // the null network to interfaces that have the null network (tethering
- // downstream interfaces).
- && Objects.equals(network, targetNetwork)) {
+ && Objects.equals(socketKey, targetSocketKey)) {
try {
socket.send(packet);
} catch (IOException e) {
@@ -241,13 +228,13 @@
}
}
- private boolean isIpv6OnlyNetworks(
+ private boolean isIpv6OnlySockets(
@NonNull ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets,
- @Nullable Network targetNetwork) {
+ @NonNull SocketKey targetSocketKey) {
for (int i = 0; i < activeSockets.size(); i++) {
final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
- final Network network = activeSockets.valueAt(i).getNetwork();
- if (Objects.equals(network, targetNetwork) && socket.hasJoinedIpv4()) {
+ final SocketKey socketKey = activeSockets.valueAt(i);
+ if (Objects.equals(socketKey, targetSocketKey) && socket.hasJoinedIpv4()) {
return false;
}
}
@@ -276,38 +263,35 @@
}
/**
- * Send a mDNS request packet via given network that asks for multicast response.
- *
- * <p>The socket client may use a null network to identify some or all interfaces, in which case
- * passing null sends the packet to these.
+ * Send a mDNS request packet via given socket key that asks for multicast response.
*/
public void sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet,
- @Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
- mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks));
+ @NonNull SocketKey socketKey, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
+ mHandler.post(() -> sendMdnsPacket(packet, socketKey, onlyUseIpv6OnIpv6OnlyNetworks));
}
@Override
public void sendPacketRequestingMulticastResponse(
@NonNull DatagramPacket packet, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
- sendPacketRequestingMulticastResponse(
- packet, null /* network */, onlyUseIpv6OnIpv6OnlyNetworks);
+ throw new UnsupportedOperationException("This socket client need to specify the socket to"
+ + "send packet");
}
/**
- * Send a mDNS request packet via given network that asks for unicast response.
+ * Send a mDNS request packet via given socket key that asks for unicast response.
*
* <p>The socket client may use a null network to identify some or all interfaces, in which case
* passing null sends the packet to these.
*/
public void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet,
- @Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
- mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks));
+ @NonNull SocketKey socketKey, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
+ mHandler.post(() -> sendMdnsPacket(packet, socketKey, onlyUseIpv6OnIpv6OnlyNetworks));
}
@Override
public void sendPacketRequestingUnicastResponse(
@NonNull DatagramPacket packet, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
- sendPacketRequestingUnicastResponse(
- packet, null /* network */, onlyUseIpv6OnIpv6OnlyNetworks);
+ throw new UnsupportedOperationException("This socket client need to specify the socket to"
+ + "send packet");
}
}
\ No newline at end of file
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 a36eb1b..48e4724 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.Network;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -262,15 +261,13 @@
/**
* Process an incoming response packet.
*/
- public synchronized void processResponse(@NonNull MdnsPacket packet, int interfaceIndex,
- Network network) {
+ public synchronized void processResponse(@NonNull MdnsPacket packet,
+ @NonNull SocketKey socketKey) {
synchronized (lock) {
// Augment the list of current known responses, and generated responses for resolve
// requests if there is no known response
final List<MdnsResponse> currentList = new ArrayList<>(instanceNameToResponse.values());
-
- List<MdnsResponse> additionalResponses = makeResponsesForResolve(interfaceIndex,
- network);
+ List<MdnsResponse> additionalResponses = makeResponsesForResolve(socketKey);
for (MdnsResponse additionalResponse : additionalResponses) {
if (!instanceNameToResponse.containsKey(
additionalResponse.getServiceInstanceName())) {
@@ -278,7 +275,8 @@
}
}
final Pair<ArraySet<MdnsResponse>, ArrayList<MdnsResponse>> augmentedResult =
- responseDecoder.augmentResponses(packet, currentList, interfaceIndex, network);
+ responseDecoder.augmentResponses(packet, currentList,
+ socketKey.getInterfaceIndex(), socketKey.getNetwork());
final ArraySet<MdnsResponse> modifiedResponse = augmentedResult.first;
final ArrayList<MdnsResponse> allResponses = augmentedResult.second;
@@ -508,8 +506,7 @@
}
}
- private List<MdnsResponse> makeResponsesForResolve(int interfaceIndex,
- @NonNull Network network) {
+ private List<MdnsResponse> makeResponsesForResolve(@NonNull SocketKey socketKey) {
final List<MdnsResponse> resolveResponses = new ArrayList<>();
for (int i = 0; i < listeners.size(); i++) {
final String resolveName = listeners.valueAt(i).getResolveInstanceName();
@@ -524,7 +521,7 @@
instanceFullName.addAll(Arrays.asList(serviceTypeLabels));
knownResponse = new MdnsResponse(
0L /* lastUpdateTime */, instanceFullName.toArray(new String[0]),
- interfaceIndex, network);
+ socketKey.getInterfaceIndex(), socketKey.getNetwork());
}
resolveResponses.add(knownResponse);
}
@@ -548,10 +545,7 @@
// The listener is requesting to resolve a service that has no info in
// cache. Use the provided name to generate a minimal response, so other records are
// queried to complete it.
- // Only the names are used to know which queries to send, other parameters like
- // interfaceIndex do not matter.
- servicesToResolve = makeResponsesForResolve(
- 0 /* interfaceIndex */, config.socketKey.getNetwork());
+ servicesToResolve = makeResponsesForResolve(config.socketKey);
sendDiscoveryQueries = servicesToResolve.size() < listeners.size();
}
Pair<Integer, List<String>> result;
@@ -564,7 +558,7 @@
config.subtypes,
config.expectUnicastResponse,
config.transactionId,
- config.socketKey.getNetwork(),
+ config.socketKey,
config.onlyUseIpv6OnIpv6OnlyNetworks,
sendDiscoveryQueries,
servicesToResolve,
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 5e4a8b5..b6000f0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java
@@ -82,6 +82,6 @@
void onSocketCreated(@NonNull SocketKey socketKey);
/*** Notify requested socket is destroyed */
- void onAllSocketsDestroyed(@NonNull SocketKey socketKey);
+ void onSocketDestroyed(@NonNull SocketKey socketKey);
}
}
\ No newline at end of file
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 3df6313..e963ab7 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -258,11 +258,6 @@
@NonNull final NetLinkMonitorCallBack cb) {
return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
}
-
- /*** Get interface index by given socket */
- public int getInterfaceIndex(@NonNull MdnsInterfaceSocket socket) {
- return socket.getInterface().getIndex();
- }
}
/**
* The callback interface for the netlink monitor messages.
@@ -323,11 +318,14 @@
final MdnsInterfaceSocket mSocket;
final List<LinkAddress> mAddresses;
final int[] mTransports;
+ @NonNull final SocketKey mSocketKey;
- SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses, int[] transports) {
+ SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses, int[] transports,
+ @NonNull SocketKey socketKey) {
mSocket = socket;
mAddresses = new ArrayList<>(addresses);
mTransports = transports;
+ mSocketKey = socketKey;
}
}
@@ -447,7 +445,7 @@
// Try to join the group again.
socketInfo.mSocket.joinGroup(addresses);
- notifyAddressesChanged(network, socketInfo.mSocket, addresses);
+ notifyAddressesChanged(network, socketInfo, addresses);
}
private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
int ifaceIndex) {
@@ -528,21 +526,22 @@
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
final List<LinkAddress> addresses = lp.getLinkAddresses();
+ final Network network =
+ networkKey == LOCAL_NET ? null : ((NetworkAsKey) networkKey).mNetwork;
+ final SocketKey socketKey = new SocketKey(network, networkInterface.getIndex());
// TODO: technically transport types are mutable, although generally not in ways that
// would meaningfully impact the logic using it here. Consider updating logic to
// support transports being added/removed.
- final SocketInfo socketInfo = new SocketInfo(socket, addresses, transports);
+ final SocketInfo socketInfo = new SocketInfo(socket, addresses, transports, socketKey);
if (networkKey == LOCAL_NET) {
mTetherInterfaceSockets.put(interfaceName, socketInfo);
} else {
- mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork, socketInfo);
+ mNetworkSockets.put(network, socketInfo);
}
// Try to join IPv4/IPv6 group.
socket.joinGroup(addresses);
// Notify the listeners which need this socket.
- final Network network =
- networkKey == LOCAL_NET ? null : ((NetworkAsKey) networkKey).mNetwork;
notifySocketCreated(network, socketInfo);
} catch (IOException e) {
mSharedLog.e("Create socket failed ifName:" + interfaceName, e);
@@ -584,7 +583,7 @@
if (socketInfo == null) return;
socketInfo.mSocket.destroy();
- notifyInterfaceDestroyed(network, socketInfo.mSocket);
+ notifyInterfaceDestroyed(network, socketInfo);
mSocketRequestMonitor.onSocketDestroyed(network, socketInfo.mSocket);
mSharedLog.log("Remove socket on net:" + network);
}
@@ -593,7 +592,7 @@
final SocketInfo socketInfo = mTetherInterfaceSockets.remove(interfaceName);
if (socketInfo == null) return;
socketInfo.mSocket.destroy();
- notifyInterfaceDestroyed(null /* network */, socketInfo.mSocket);
+ notifyInterfaceDestroyed(null /* network */, socketInfo);
mSocketRequestMonitor.onSocketDestroyed(null /* network */, socketInfo.mSocket);
mSharedLog.log("Remove socket on ifName:" + interfaceName);
}
@@ -602,9 +601,7 @@
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) {
- final int ifaceIndex = mDependencies.getInterfaceIndex(socketInfo.mSocket);
- final SocketKey socketKey = new SocketKey(network, ifaceIndex);
- mCallbacksToRequestedNetworks.keyAt(i).onSocketCreated(socketKey,
+ mCallbacksToRequestedNetworks.keyAt(i).onSocketCreated(socketInfo.mSocketKey,
socketInfo.mSocket, socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(network, socketInfo.mSocket,
socketInfo.mTransports);
@@ -612,25 +609,23 @@
}
}
- private void notifyInterfaceDestroyed(Network network, MdnsInterfaceSocket socket) {
+ private void notifyInterfaceDestroyed(Network network, SocketInfo socketInfo) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) {
- final int ifaceIndex = mDependencies.getInterfaceIndex(socket);
mCallbacksToRequestedNetworks.keyAt(i)
- .onInterfaceDestroyed(new SocketKey(network, ifaceIndex), socket);
+ .onInterfaceDestroyed(socketInfo.mSocketKey, socketInfo.mSocket);
}
}
}
- private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
+ private void notifyAddressesChanged(Network network, SocketInfo socketInfo,
List<LinkAddress> addresses) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) {
- final int ifaceIndex = mDependencies.getInterfaceIndex(socket);
mCallbacksToRequestedNetworks.keyAt(i)
- .onAddressesChanged(new SocketKey(network, ifaceIndex), socket, addresses);
+ .onAddressesChanged(socketInfo.mSocketKey, socketInfo.mSocket, addresses);
}
}
}
@@ -647,9 +642,7 @@
createSocket(new NetworkAsKey(network), lp);
} else {
// Notify the socket for requested network.
- final int ifaceIndex = mDependencies.getInterfaceIndex(socketInfo.mSocket);
- final SocketKey socketKey = new SocketKey(network, ifaceIndex);
- cb.onSocketCreated(socketKey, socketInfo.mSocket, socketInfo.mAddresses);
+ cb.onSocketCreated(socketInfo.mSocketKey, socketInfo.mSocket, socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(network, socketInfo.mSocket,
socketInfo.mTransports);
}
@@ -664,9 +657,7 @@
createLPForTetheredInterface(interfaceName, ifaceIndex));
} else {
// Notify the socket for requested network.
- final int ifaceIndex = mDependencies.getInterfaceIndex(socketInfo.mSocket);
- final SocketKey socketKey = new SocketKey(ifaceIndex);
- cb.onSocketCreated(socketKey, socketInfo.mSocket, socketInfo.mAddresses);
+ cb.onSocketCreated(socketInfo.mSocketKey, socketInfo.mSocket, socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(null /* socketNetwork */,
socketInfo.mSocket, socketInfo.mTransports);
}
diff --git a/service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java b/service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java
index 0ecae48..48c396e 100644
--- a/service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java
+++ b/service-t/src/com/android/server/connectivity/mdns/NetworkInterfaceWrapper.java
@@ -57,6 +57,10 @@
return networkInterface.getInterfaceAddresses();
}
+ public int getIndex() {
+ return networkInterface.getIndex();
+ }
+
@Override
public String toString() {
return networkInterface.toString();
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketKey.java b/service-t/src/com/android/server/connectivity/mdns/SocketKey.java
index a893acb..f13d0e0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/SocketKey.java
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketKey.java
@@ -43,6 +43,7 @@
mInterfaceIndex = interfaceIndex;
}
+ @Nullable
public Network getNetwork() {
return mNetwork;
}
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 22d9b01..f30abc6 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -135,10 +135,17 @@
<!-- Whether to cancel network notifications automatically when tapped -->
<bool name="config_autoCancelNetworkNotifications">true</bool>
- <!-- When no internet or partial connectivity is detected on a network, and a high priority
- (heads up) notification would be shown due to the network being explicitly selected,
- directly show the dialog that would normally be shown when tapping the notification
- instead of showing the notification. -->
+ <!-- Configuration to let OEMs customize what to do when :
+ • Partial connectivity is detected on the network
+ • No internet is detected on the network, and
+ - the network was explicitly selected
+ - the system is configured to actively prefer bad wifi (see config_activelyPreferBadWifi)
+ The default behavior (false) is to post a notification with a PendingIntent so
+ the user is informed and can act if they wish.
+ Making this true instead will have the system fire the intent immediately instead
+ of showing a notification. OEMs who do this should have some intent receiver
+ listening to the intent and take the action they prefer (e.g. show a dialog,
+ show a customized notification etc). -->
<bool name="config_notifyNoInternetAsDialogWhenHighPriority">false</bool>
<!-- When showing notifications indicating partial connectivity, display the same notifications
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 53127d1..83afa83 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -318,6 +318,7 @@
import java.io.InterruptedIOException;
import java.io.PrintWriter;
import java.io.Writer;
+import java.lang.IllegalArgumentException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -389,7 +390,7 @@
// Timeout in case the "actively prefer bad wifi" feature is on
private static final int ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS = 20 * 1000;
// Timeout in case the "actively prefer bad wifi" feature is off
- private static final int DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS = 8 * 1000;
+ private static final int DEFAULT_EVALUATION_TIMEOUT_MS = 8 * 1000;
// Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
@@ -4010,7 +4011,7 @@
// the destroyed flag is only just above the "current satisfier wins"
// tie-breaker. But technically anything that affects scoring should rematch.
rematchAllNetworksAndRequests();
- mHandler.postDelayed(() -> disconnectAndDestroyNetwork(nai), timeoutMs);
+ mHandler.postDelayed(() -> nai.disconnect(), timeoutMs);
break;
}
}
@@ -4547,9 +4548,11 @@
@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();
+ // The feature of creating the networks immediately was slated for U, but race conditions
+ // detected late required this was flagged off.
+ // TODO : enable this in a Mainline update or in V, and re-enable the test for this
+ // in NetworkAgentTest.
+ return false;
}
private static boolean shouldCreateNativeNetwork(@NonNull NetworkAgentInfo nai,
@@ -4609,9 +4612,6 @@
if (DBG) {
log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
-
- nai.disconnect();
-
// Clear all notifications of this network.
mNotifier.clearNotification(nai.network.getNetId());
// A network agent has disconnected.
@@ -5897,7 +5897,7 @@
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
if (nai == null) break;
nai.onPreventAutomaticReconnect();
- disconnectAndDestroyNetwork(nai);
+ nai.disconnect();
break;
case EVENT_SET_VPN_NETWORK_PREFERENCE:
handleSetVpnNetworkPreference((VpnNetworkPreferenceInfo) msg.obj);
@@ -9042,7 +9042,7 @@
break;
}
}
- disconnectAndDestroyNetwork(nai);
+ nai.disconnect();
}
private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
@@ -9584,10 +9584,7 @@
updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);
// Tear down all unneeded networks.
- // Iterate in reverse order because teardownUnneededNetwork removes the nai from
- // mNetworkAgentInfos.
- for (int i = mNetworkAgentInfos.size() - 1; i >= 0; i--) {
- final NetworkAgentInfo nai = mNetworkAgentInfos.valueAt(i);
+ for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
if (nai.getInactivityExpiry() > 0) {
// This network has active linger timers and no requests, but is not
@@ -9940,10 +9937,25 @@
networkAgent.networkMonitor().notifyNetworkConnected(params.linkProperties,
params.networkCapabilities);
}
- final long delay = !avoidBadWifi() && activelyPreferBadWifi()
- ? ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS
- : DONT_ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS;
- scheduleEvaluationTimeout(networkAgent.network, delay);
+ final long evaluationDelay;
+ if (!networkAgent.networkCapabilities.hasSingleTransport(TRANSPORT_WIFI)) {
+ // If the network is anything other than pure wifi, use the default timeout.
+ evaluationDelay = DEFAULT_EVALUATION_TIMEOUT_MS;
+ } else if (networkAgent.networkAgentConfig.isExplicitlySelected()) {
+ // If the network is explicitly selected, use the default timeout because it's
+ // shorter and the user is likely staring at the screen expecting it to validate
+ // right away.
+ evaluationDelay = DEFAULT_EVALUATION_TIMEOUT_MS;
+ } else if (avoidBadWifi() || !activelyPreferBadWifi()) {
+ // If avoiding bad wifi, or if not avoiding but also not preferring bad wifi
+ evaluationDelay = DEFAULT_EVALUATION_TIMEOUT_MS;
+ } else {
+ // It's wifi, automatically connected, and bad wifi is preferred : use the
+ // longer timeout to avoid the device switching to captive portals with bad
+ // signal or very slow response.
+ evaluationDelay = ACTIVELY_PREFER_BAD_WIFI_INITIAL_TIMEOUT_MS;
+ }
+ scheduleEvaluationTimeout(networkAgent.network, evaluationDelay);
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
// be communicated to a particular NetworkAgent depends only on the network's immutable,
@@ -9970,6 +9982,7 @@
// This has to happen after matching the requests, because callbacks are just requests.
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
} else if (state == NetworkInfo.State.DISCONNECTED) {
+ networkAgent.disconnect();
if (networkAgent.isVPN()) {
updateVpnUids(networkAgent, networkAgent.networkCapabilities, null);
}
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 8b0cb7c..bc13592 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -322,7 +322,8 @@
private boolean maybeNotifyViaDialog(Resources res, NotificationType notifyType,
PendingIntent intent) {
- if (notifyType != NotificationType.NO_INTERNET
+ if (notifyType != NotificationType.LOST_INTERNET
+ && notifyType != NotificationType.NO_INTERNET
&& notifyType != NotificationType.PARTIAL_CONNECTIVITY) {
return false;
}
@@ -432,7 +433,8 @@
* A notification with a higher number will take priority over a notification with a lower
* number.
*/
- private static int priority(NotificationType t) {
+ @VisibleForTesting
+ public static int priority(NotificationType t) {
if (t == null) {
return 0;
}
diff --git a/service/src/com/android/server/connectivity/ProxyTracker.java b/service/src/com/android/server/connectivity/ProxyTracker.java
index bda4b8f..6a0918b 100644
--- a/service/src/com/android/server/connectivity/ProxyTracker.java
+++ b/service/src/com/android/server/connectivity/ProxyTracker.java
@@ -86,6 +86,7 @@
private final Handler mConnectivityServiceHandler;
+ @Nullable
private final PacProxyManager mPacProxyManager;
private class PacProxyInstalledListener implements PacProxyManager.PacProxyInstalledListener {
@@ -109,9 +110,11 @@
mConnectivityServiceHandler = connectivityServiceInternalHandler;
mPacProxyManager = context.getSystemService(PacProxyManager.class);
- PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
- mPacProxyManager.addPacProxyInstalledListener(
+ if (mPacProxyManager != null) {
+ PacProxyInstalledListener listener = new PacProxyInstalledListener(pacChangedEvent);
+ mPacProxyManager.addPacProxyInstalledListener(
mConnectivityServiceHandler::post, listener);
+ }
}
// Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
@@ -205,7 +208,7 @@
mGlobalProxy = proxyProperties;
}
- if (!TextUtils.isEmpty(pacFileUrl)) {
+ if (!TextUtils.isEmpty(pacFileUrl) && mPacProxyManager != null) {
mConnectivityServiceHandler.post(
() -> mPacProxyManager.setCurrentProxyScriptUrl(proxyProperties));
}
@@ -251,7 +254,10 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
- mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
+
+ if (mPacProxyManager != null) {
+ mPacProxyManager.setCurrentProxyScriptUrl(proxyInfo);
+ }
if (!shouldSendBroadcast(proxyInfo)) {
return;
diff --git a/tests/common/OWNERS b/tests/common/OWNERS
new file mode 100644
index 0000000..3101da5
--- /dev/null
+++ b/tests/common/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
\ No newline at end of file
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/common/java/android/net/nsd/NsdServiceInfoTest.java
similarity index 88%
rename from tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
rename to tests/common/java/android/net/nsd/NsdServiceInfoTest.java
index 9ce0693..ffe0e91 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/common/java/android/net/nsd/NsdServiceInfoTest.java
@@ -26,10 +26,10 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
-import android.os.StrictMode;
import androidx.test.filters.SmallTest;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -37,7 +37,6 @@
import org.junit.runner.RunWith;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -45,22 +44,11 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+@ConnectivityModuleTest
public class NsdServiceInfoTest {
private static final InetAddress IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
private static final InetAddress IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::");
- public final static InetAddress LOCALHOST;
- static {
- // Because test.
- StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
- StrictMode.setThreadPolicy(policy);
-
- InetAddress _host = null;
- try {
- _host = InetAddress.getLocalHost();
- } catch (UnknownHostException e) { }
- LOCALHOST = _host;
- }
@Test
public void testLimits() throws Exception {
@@ -89,10 +77,10 @@
// Single key + value length too long.
exceptionThrown = false;
try {
- String longValue = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
- "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
- "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
- "ooooooooooooooooooooooooooooong"; // 248 characters.
+ String longValue = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ + "ooooooooooooooooooooooooooooong"; // 248 characters.
info.setAttribute("longcat", longValue); // Key + value == 255 characters.
} catch (IllegalArgumentException e) {
exceptionThrown = true;
@@ -127,7 +115,6 @@
fullInfo.setServiceName("kitten");
fullInfo.setServiceType("_kitten._tcp");
fullInfo.setPort(4242);
- fullInfo.setHost(LOCALHOST);
fullInfo.setHostAddresses(List.of(IPV4_ADDRESS));
fullInfo.setNetwork(new Network(123));
fullInfo.setInterfaceIndex(456);
@@ -143,8 +130,7 @@
attributedInfo.setServiceName("kitten");
attributedInfo.setServiceType("_kitten._tcp");
attributedInfo.setPort(4242);
- attributedInfo.setHost(LOCALHOST);
- fullInfo.setHostAddresses(List.of(IPV6_ADDRESS, IPV4_ADDRESS));
+ attributedInfo.setHostAddresses(List.of(IPV6_ADDRESS, IPV4_ADDRESS));
attributedInfo.setAttribute("color", "pink");
attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8"));
attributedInfo.setAttribute("adorable", (String) null);
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 8388cb7..8c2408b 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,4 +1,5 @@
# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
set noparent
file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 03e74e0..a0508e1 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -847,7 +847,7 @@
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
- Network wifiNetwork = mCtsNetUtils.connectToWifi();
+ Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
Network cellNetwork = mCtsNetUtils.connectToCell();
// This server returns the requestor's IP address as the response body.
URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
@@ -3152,7 +3152,8 @@
}
@AppModeFull(reason = "Need WiFi support to test the default active network")
- @Test
+ // NetworkActivityTracker is not mainlined before S.
+ @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public void testDefaultNetworkActiveListener() throws Exception {
final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -3236,7 +3237,8 @@
newMobileDataPreferredUids.add(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- waitForAvailable(defaultTrackingCb, cellNetwork);
+ defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> cellNetwork.equals(entry.getNetwork()));
// No change for system default network. Expect no callback except CapabilitiesChanged
// or LinkPropertiesChanged which may be triggered randomly from wifi network.
assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
@@ -3248,7 +3250,8 @@
newMobileDataPreferredUids.remove(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- waitForAvailable(defaultTrackingCb, wifiNetwork);
+ defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> wifiNetwork.equals(entry.getNetwork()));
// No change for system default network. Expect no callback except CapabilitiesChanged
// or LinkPropertiesChanged which may be triggered randomly from wifi network.
assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 9f8a05d..98ea224 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -164,6 +164,12 @@
it.obj = obj
}
+// On T and below, the native network is only created when the agent connects.
+// Starting in U, the native network was to be created as soon as the agent is registered,
+// but this has been flagged off for now pending resolution of race conditions.
+// TODO : enable this in a Mainline update or in V.
+private const val SHOULD_CREATE_NETWORKS_IMMEDIATELY = false
+
@RunWith(DevSdkIgnoreRunner::class)
// NetworkAgent is not updatable in R-, so this test does not need to be compatible with older
// versions. NetworkAgent was also based on AsyncChannel before S so cannot be tested the same way.
@@ -1247,15 +1253,15 @@
// Connect a third network. Because network1 is awaiting replacement, network3 is preferred
// as soon as it validates (until then, it is outscored by network1).
- // The fact that the first event seen by matchAllCallback is the connection of network3
+ // The fact that the first events seen by matchAllCallback is the connection of network3
// implicitly ensures that no callbacks are sent since network1 was lost.
val (agent3, network3) = connectNetwork()
+ matchAllCallback.expectAvailableThenValidatedCallbacks(network3)
+ testCallback.expectAvailableDoubleValidatedCallbacks(network3)
+
// As soon as the replacement arrives, network1 is disconnected.
// Check that this happens before the replacement timeout (5 seconds) fires.
- matchAllCallback.expectAvailableCallbacks(network3, validated = false)
matchAllCallback.expect<Lost>(network1, 2_000 /* timeoutMs */)
- matchAllCallback.expectCaps(network3) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
- testCallback.expectAvailableDoubleValidatedCallbacks(network3)
agent1.expectCallback<OnNetworkUnwanted>()
// Test lingering:
@@ -1301,8 +1307,8 @@
val callback = TestableNetworkCallback()
requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
val agent6 = createNetworkAgent(specifier = specifier6)
- agent6.register()
- if (SdkLevel.isAtLeastU()) {
+ val network6 = agent6.register()
+ if (SHOULD_CREATE_NETWORKS_IMMEDIATELY) {
agent6.expectCallback<OnNetworkCreated>()
} else {
// No callbacks are sent, so check LinkProperties to wait for the network to be created.
@@ -1316,9 +1322,10 @@
val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
agent6.unregisterAfterReplacement(timeoutMs)
agent6.expectCallback<OnNetworkUnwanted>()
- if (!SdkLevel.isAtLeastT() || SdkLevel.isAtLeastU()) {
+ if (!SdkLevel.isAtLeastT() || SHOULD_CREATE_NETWORKS_IMMEDIATELY) {
// Before T, onNetworkDestroyed is called even if the network was never created.
- // On U+, the network was created by register(). Destroying it sends onNetworkDestroyed.
+ // If immediate native network creation is supported, the network was created by
+ // register(). Destroying it sends onNetworkDestroyed.
agent6.expectCallback<OnNetworkDestroyed>()
}
// Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
@@ -1368,9 +1375,8 @@
val (newWifiAgent, newWifiNetwork) = connectNetwork(TRANSPORT_WIFI)
testCallback.expectAvailableCallbacks(newWifiNetwork, validated = true)
- matchAllCallback.expectAvailableCallbacks(newWifiNetwork, validated = false)
+ matchAllCallback.expectAvailableThenValidatedCallbacks(newWifiNetwork)
matchAllCallback.expect<Lost>(wifiNetwork)
- matchAllCallback.expectCaps(newWifiNetwork) { it.hasCapability(NET_CAPABILITY_VALIDATED) }
wifiAgent.expectCallback<OnNetworkUnwanted>()
}
@@ -1478,10 +1484,9 @@
@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))
+ doTestNativeNetworkCreation(
+ expectCreatedImmediately = SHOULD_CREATE_NETWORKS_IMMEDIATELY,
+ intArrayOf(TRANSPORT_CELLULAR))
}
@Test
diff --git a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
index 4854901..b462f71 100644
--- a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
@@ -44,12 +44,15 @@
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.RequiredFeatureRule;
+
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.TestHttpServer;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,6 +79,11 @@
private ServerSocket mServerSocket;
private Instrumentation mInstrumentation;
+ // Devices without WebView/JavaScript cannot support PAC proxies.
+ @Rule
+ public RequiredFeatureRule mRequiredWebviewFeatureRule =
+ new RequiredFeatureRule(PackageManager.FEATURE_WEBVIEW);
+
private static final String PAC_FILE = "function FindProxyForURL(url, host)"
+ "{"
+ " return \"PROXY 192.168.0.1:9091\";"
@@ -152,9 +160,6 @@
@AppModeFull(reason = "Instant apps can't bind sockets to localhost for a test proxy server")
@Test
public void testSetCurrentProxyScriptUrl() throws Exception {
- // Devices without WebView/JavaScript cannot support PAC proxies
- assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW));
-
// Register a PacProxyInstalledListener
final TestPacProxyInstalledListener listener = new TestPacProxyInstalledListener();
final Executor executor = (Runnable r) -> r.run();
diff --git a/tests/cts/net/src/android/net/cts/RateLimitTest.java b/tests/cts/net/src/android/net/cts/RateLimitTest.java
index 36b98fc..5c93738 100644
--- a/tests/cts/net/src/android/net/cts/RateLimitTest.java
+++ b/tests/cts/net/src/android/net/cts/RateLimitTest.java
@@ -36,6 +36,7 @@
import android.icu.text.MessageFormat;
import android.net.ConnectivityManager;
import android.net.ConnectivitySettingsManager;
+import android.net.ConnectivityThread;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -189,7 +190,19 @@
// whatever happens, don't leave the device in rate limited state.
ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, -1);
}
- if (mSocket != null) mSocket.close();
+ if (mSocket == null) {
+ // HACK(b/272147742): dump ConnectivityThread if test initialization failed.
+ final StackTraceElement[] elements = ConnectivityThread.get().getStackTrace();
+ final StringBuilder sb = new StringBuilder();
+ // Skip first element as it includes the invocation of getStackTrace()
+ for (int i = 1; i < elements.length; i++) {
+ sb.append(elements[i]);
+ sb.append("\n");
+ }
+ Log.e(TAG, sb.toString());
+ } else {
+ mSocket.close();
+ }
if (mNetworkAgent != null) mNetworkAgent.unregister();
if (mTunInterface != null) mTunInterface.getFileDescriptor().close();
if (mCm != null) mCm.unregisterNetworkCallback(mNetworkCallback);
diff --git a/tests/integration/OWNERS b/tests/integration/OWNERS
new file mode 100644
index 0000000..3101da5
--- /dev/null
+++ b/tests/integration/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
\ No newline at end of file
diff --git a/tests/mts/OWNERS b/tests/mts/OWNERS
new file mode 100644
index 0000000..3101da5
--- /dev/null
+++ b/tests/mts/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
\ No newline at end of file
diff --git a/tests/native/connectivity_native_test/OWNERS b/tests/native/connectivity_native_test/OWNERS
index 8dfa455..fbfcf92 100644
--- a/tests/native/connectivity_native_test/OWNERS
+++ b/tests/native/connectivity_native_test/OWNERS
@@ -1,3 +1,4 @@
-# Bug component: 31808
+# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
set noparent
file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/unit/OWNERS b/tests/unit/OWNERS
new file mode 100644
index 0000000..3101da5
--- /dev/null
+++ b/tests/unit/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: http://b/new?component=31808
+# TODO: move bug template config to common owners file once b/226427845 is resolved
\ No newline at end of file
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 193078d..bbde9b4 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2274,12 +2274,12 @@
mDestroySocketsWrapper.destroyLiveTcpSocketsByOwnerUids(ownerUids);
}
- final ArrayTrackRecord<Long>.ReadHead mScheduledEvaluationTimeouts =
- new ArrayTrackRecord<Long>().newReadHead();
+ final ArrayTrackRecord<Pair<Integer, Long>>.ReadHead mScheduledEvaluationTimeouts =
+ new ArrayTrackRecord<Pair<Integer, Long>>().newReadHead();
@Override
public void scheduleEvaluationTimeout(@NonNull Handler handler,
@NonNull final Network network, final long delayMs) {
- mScheduledEvaluationTimeouts.add(delayMs);
+ mScheduledEvaluationTimeouts.add(new Pair<>(network.netId, delayMs));
super.scheduleEvaluationTimeout(handler, network, delayMs);
}
}
@@ -2973,24 +2973,22 @@
if (expectLingering) {
generalCb.expectLosing(net1);
}
+ generalCb.expectCaps(net2, c -> c.hasCapability(NET_CAPABILITY_VALIDATED));
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
// Make sure cell 1 is unwanted immediately if the radio can't time share, but only
// after some delay if it can.
if (expectLingering) {
- generalCb.expectCaps(net2, c -> c.hasCapability(NET_CAPABILITY_VALIDATED));
- defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
net1.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // always incurs the timeout
generalCb.assertNoCallback();
// assertNotDisconnected waited for TEST_CALLBACK_TIMEOUT_MS, so waiting for the
// linger period gives TEST_CALLBACK_TIMEOUT_MS time for the event to process.
net1.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
- generalCb.expect(LOST, net1);
} else {
net1.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
- generalCb.expect(LOST, net1);
- generalCb.expectCaps(net2, c -> c.hasCapability(NET_CAPABILITY_VALIDATED));
- defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
}
+ net1.disconnect();
+ generalCb.expect(LOST, net1);
// Remove primary from net 2
net2.setScore(new NetworkScore.Builder().build());
@@ -6153,7 +6151,7 @@
}
public void doTestPreferBadWifi(final boolean avoidBadWifi,
- final boolean preferBadWifi,
+ final boolean preferBadWifi, final boolean explicitlySelected,
@NonNull Predicate<Long> checkUnvalidationTimeout) throws Exception {
// Pretend we're on a carrier that restricts switching away from bad wifi, and
// depending on the parameter one that may indeed prefer bad wifi.
@@ -6177,10 +6175,13 @@
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellAgent);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiAgent.explicitlySelected(explicitlySelected, false /* acceptUnvalidated */);
mWiFiAgent.connect(false);
wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
- mDeps.mScheduledEvaluationTimeouts.poll(TIMEOUT_MS, t -> checkUnvalidationTimeout.test(t));
+ assertNotNull(mDeps.mScheduledEvaluationTimeouts.poll(TIMEOUT_MS,
+ t -> t.first == mWiFiAgent.getNetwork().netId
+ && checkUnvalidationTimeout.test(t.second)));
if (!avoidBadWifi && preferBadWifi) {
expectUnvalidationCheckWillNotify(mWiFiAgent, NotificationType.LOST_INTERNET);
@@ -6196,27 +6197,33 @@
// Starting with U this mode is no longer supported and can't actually be tested
assumeFalse(mDeps.isAtLeastU());
doTestPreferBadWifi(false /* avoidBadWifi */, false /* preferBadWifi */,
- timeout -> timeout < 14_000);
+ false /* explicitlySelected */, timeout -> timeout < 14_000);
}
@Test
- public void testPreferBadWifi_doNotAvoid_doPrefer() throws Exception {
+ public void testPreferBadWifi_doNotAvoid_doPrefer_notExplicit() throws Exception {
doTestPreferBadWifi(false /* avoidBadWifi */, true /* preferBadWifi */,
- timeout -> timeout > 14_000);
+ false /* explicitlySelected */, timeout -> timeout > 14_000);
+ }
+
+ @Test
+ public void testPreferBadWifi_doNotAvoid_doPrefer_explicitlySelected() throws Exception {
+ doTestPreferBadWifi(false /* avoidBadWifi */, true /* preferBadWifi */,
+ true /* explicitlySelected */, timeout -> timeout < 14_000);
}
@Test
public void testPreferBadWifi_doAvoid_doNotPrefer() throws Exception {
// If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
doTestPreferBadWifi(true /* avoidBadWifi */, false /* preferBadWifi */,
- timeout -> timeout < 14_000);
+ false /* explicitlySelected */, timeout -> timeout < 14_000);
}
@Test
public void testPreferBadWifi_doAvoid_doPrefer() throws Exception {
// If avoidBadWifi=true, then preferBadWifi should be irrelevant. Test anyway.
doTestPreferBadWifi(true /* avoidBadWifi */, true /* preferBadWifi */,
- timeout -> timeout < 14_000);
+ false /* explicitlySelected */, timeout -> timeout < 14_000);
}
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index a27a0bf..967083e 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -62,6 +62,7 @@
import android.util.DisplayMetrics;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.test.filters.SmallTest;
@@ -386,14 +387,37 @@
}
@Test
- public void testNotifyNoInternetAsDialogWhenHighPriority() throws Exception {
- doReturn(true).when(mResources).getBoolean(
+ public void testNotifyNoInternet_asNotification() throws Exception {
+ doTestNotifyNotificationAsDialogWhenHighPriority(false, NO_INTERNET);
+ }
+ @Test
+ public void testNotifyNoInternet_asDialog() throws Exception {
+ doTestNotifyNotificationAsDialogWhenHighPriority(true, NO_INTERNET);
+ }
+
+ @Test
+ public void testNotifyLostInternet_asNotification() throws Exception {
+ doTestNotifyNotificationAsDialogWhenHighPriority(false, LOST_INTERNET);
+ }
+
+ @Test
+ public void testNotifyLostInternet_asDialog() throws Exception {
+ doTestNotifyNotificationAsDialogWhenHighPriority(true, LOST_INTERNET);
+ }
+
+ public void doTestNotifyNotificationAsDialogWhenHighPriority(final boolean configActive,
+ @NonNull final NotificationType notifType) throws Exception {
+ doReturn(configActive).when(mResources).getBoolean(
R.bool.config_notifyNoInternetAsDialogWhenHighPriority);
final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
final UiDevice uiDevice = UiDevice.getInstance(instr);
final Context ctx = instr.getContext();
final PowerManager pm = ctx.getSystemService(PowerManager.class);
+ // If the prio of this notif is < that of NETWORK_SWITCH, it's the lowest prio and
+ // therefore it can't be tested whether it cancels other lower-prio notifs.
+ final boolean isLowestPrioNotif = NetworkNotificationManager.priority(notifType)
+ < NetworkNotificationManager.priority(NETWORK_SWITCH);
// Wake up the device (it has no effect if the device is already awake).
uiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
@@ -409,9 +433,13 @@
uiDevice.wait(Until.hasObject(By.pkg(launcherPackageName)),
UI_AUTOMATOR_WAIT_TIME_MILLIS));
- mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
- // Non-"no internet" notifications are not affected
- verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any());
+ if (!isLowestPrioNotif) {
+ mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai,
+ null, false);
+ // Non-"no internet" notifications are not affected
+ verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId),
+ any());
+ }
final String testAction = "com.android.connectivity.coverage.TEST_DIALOG";
final Intent intent = new Intent(testAction)
@@ -420,22 +448,30 @@
final PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0 /* requestCode */,
intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- mManager.showNotification(TEST_NOTIF_ID, NO_INTERNET, mWifiNai, null /* switchToNai */,
+ mManager.showNotification(TEST_NOTIF_ID, notifType, mWifiNai, null /* switchToNai */,
pendingIntent, true /* highPriority */);
- // Previous notifications are still dismissed
- verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId);
+ if (!isLowestPrioNotif) {
+ // Previous notifications are still dismissed
+ verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId);
+ }
- // Verify that the activity is shown (the activity shows the action on screen)
- final UiObject actionText = uiDevice.findObject(new UiSelector().text(testAction));
- assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS));
+ if (configActive) {
+ // Verify that the activity is shown (the activity shows the action on screen)
+ final UiObject actionText = uiDevice.findObject(new UiSelector().text(testAction));
+ assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS));
- // Tapping the text should dismiss the dialog
- actionText.click();
- assertTrue("Activity not dismissed", actionText.waitUntilGone(TEST_TIMEOUT_MS));
+ // Tapping the text should dismiss the dialog
+ actionText.click();
+ assertTrue("Activity not dismissed", actionText.waitUntilGone(TEST_TIMEOUT_MS));
- // Verify no NO_INTERNET notification was posted
- verify(mNotificationManager, never()).notify(any(), eq(NO_INTERNET.eventId), any());
+ // Verify that the notification was not posted
+ verify(mNotificationManager, never()).notify(any(), eq(notifType.eventId), any());
+ } else {
+ // Notification should have been posted, and will have overridden the previous
+ // one because it has the same id (hence no cancel).
+ verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(notifType.eventId), any());
+ }
}
private void doNotificationTextTest(NotificationType type, @StringRes int expectedTitleRes,
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 c467f45..6a0334f 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt
@@ -56,8 +56,8 @@
private val TEST_ADDR = parseNumericAddress("2001:db8::123")
private val TEST_LINKADDR = LinkAddress(TEST_ADDR, 64 /* prefixLength */)
private val TEST_NETWORK_1 = mock(Network::class.java)
-private val TEST_SOCKETKEY_1 = mock(SocketKey::class.java)
-private val TEST_SOCKETKEY_2 = mock(SocketKey::class.java)
+private val TEST_SOCKETKEY_1 = SocketKey(1001 /* interfaceIndex */)
+private val TEST_SOCKETKEY_2 = SocketKey(1002 /* interfaceIndex */)
private val TEST_HOSTNAME = arrayOf("Android_test", "local")
private const val TEST_SUBTYPE = "_subtype"
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 d2298fe..1a4ae5d 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java
@@ -19,7 +19,6 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -212,31 +211,31 @@
runOnHandler(() -> discoveryManager.onResponseReceived(
responseForServiceTypeOne, SOCKET_KEY_NULL_NETWORK));
// Packets for network null are only processed by the ServiceTypeClient for network null
- verify(mockServiceTypeClientType1NullNetwork).processResponse(responseForServiceTypeOne,
- SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
- verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(), any());
- verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(), any());
+ verify(mockServiceTypeClientType1NullNetwork).processResponse(
+ responseForServiceTypeOne, SOCKET_KEY_NULL_NETWORK);
+ verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), any());
+ verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), any());
final MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2);
runOnHandler(() -> discoveryManager.onResponseReceived(
responseForServiceTypeTwo, SOCKET_KEY_NETWORK_1));
- verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(),
- eq(SOCKET_KEY_NETWORK_1.getNetwork()));
- verify(mockServiceTypeClientType1Network1).processResponse(responseForServiceTypeTwo,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
- verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(),
- eq(SOCKET_KEY_NETWORK_1.getNetwork()));
+ verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(),
+ eq(SOCKET_KEY_NETWORK_1));
+ verify(mockServiceTypeClientType1Network1).processResponse(
+ responseForServiceTypeTwo, SOCKET_KEY_NETWORK_1);
+ verify(mockServiceTypeClientType2Network2, never()).processResponse(any(),
+ eq(SOCKET_KEY_NETWORK_1));
final MdnsPacket responseForSubtype =
createMdnsPacket("subtype._sub._googlecast._tcp.local");
runOnHandler(() -> discoveryManager.onResponseReceived(
responseForSubtype, SOCKET_KEY_NETWORK_2));
- verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(),
- eq(SOCKET_KEY_NETWORK_2.getNetwork()));
- verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(),
- eq(SOCKET_KEY_NETWORK_2.getNetwork()));
- verify(mockServiceTypeClientType2Network2).processResponse(responseForSubtype,
- SOCKET_KEY_NETWORK_2.getInterfaceIndex(), SOCKET_KEY_NETWORK_2.getNetwork());
+ verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(),
+ eq(SOCKET_KEY_NETWORK_2));
+ verify(mockServiceTypeClientType1Network1, never()).processResponse(any(),
+ eq(SOCKET_KEY_NETWORK_2));
+ verify(mockServiceTypeClientType2Network2).processResponse(
+ responseForSubtype, SOCKET_KEY_NETWORK_2);
}
@Test
@@ -260,15 +259,13 @@
// Receive a response, it should be processed on both clients.
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
- verify(mockServiceTypeClientType1Network1).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
- verify(mockServiceTypeClientType2Network1).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
+ verify(mockServiceTypeClientType1Network1).processResponse(response, SOCKET_KEY_NETWORK_1);
+ verify(mockServiceTypeClientType2Network1).processResponse(response, SOCKET_KEY_NETWORK_1);
// The first callback receives a notification that the network has been destroyed,
// mockServiceTypeClientOne1 should send service removed notifications and remove from the
// list of clients.
- runOnHandler(() -> callback.onAllSocketsDestroyed(SOCKET_KEY_NETWORK_1));
+ runOnHandler(() -> callback.onSocketDestroyed(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1Network1).notifySocketDestroyed();
// Receive a response again, it should be processed only on
@@ -276,23 +273,23 @@
// removed from the list of clients, it is no longer able to process responses.
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
// Still times(1) as a response was received once previously
- verify(mockServiceTypeClientType1Network1, times(1)).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
- verify(mockServiceTypeClientType2Network1, times(2)).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
+ verify(mockServiceTypeClientType1Network1, times(1)).processResponse(
+ response, SOCKET_KEY_NETWORK_1);
+ verify(mockServiceTypeClientType2Network1, times(2)).processResponse(
+ response, SOCKET_KEY_NETWORK_1);
// The client for NETWORK_1 receives the callback that the NETWORK_2 has been destroyed,
// mockServiceTypeClientTwo2 shouldn't send any notifications.
- runOnHandler(() -> callback2.onAllSocketsDestroyed(SOCKET_KEY_NETWORK_2));
+ runOnHandler(() -> callback2.onSocketDestroyed(SOCKET_KEY_NETWORK_2));
verify(mockServiceTypeClientType2Network1, never()).notifySocketDestroyed();
// Receive a response again, mockServiceTypeClientType2Network1 is still in the list of
// clients, it's still able to process responses.
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
- verify(mockServiceTypeClientType1Network1, times(1)).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
- verify(mockServiceTypeClientType2Network1, times(3)).processResponse(response,
- SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
+ verify(mockServiceTypeClientType1Network1, times(1)).processResponse(
+ response, SOCKET_KEY_NETWORK_1);
+ verify(mockServiceTypeClientType2Network1, times(3)).processResponse(
+ response, SOCKET_KEY_NETWORK_1);
}
@Test
@@ -310,17 +307,17 @@
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1;
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NULL_NETWORK));
- verify(mockServiceTypeClientType1NullNetwork).processResponse(response,
- SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
+ verify(mockServiceTypeClientType1NullNetwork).processResponse(
+ response, SOCKET_KEY_NULL_NETWORK);
- runOnHandler(() -> callback.onAllSocketsDestroyed(SOCKET_KEY_NULL_NETWORK));
+ runOnHandler(() -> callback.onSocketDestroyed(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).notifySocketDestroyed();
// Receive a response again, it should not be processed.
runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NULL_NETWORK));
// Still times(1) as a response was received once previously
- verify(mockServiceTypeClientType1NullNetwork, times(1)).processResponse(response,
- SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
+ verify(mockServiceTypeClientType1NullNetwork, times(1)).processResponse(
+ response, SOCKET_KEY_NULL_NETWORK);
// Unregister the listener, notifyNetworkUnrequested should be called but other stop methods
// won't be call because the service type client was unregistered and destroyed. But those
@@ -329,7 +326,7 @@
verify(socketClient).notifyNetworkUnrequested(mockListenerOne);
verify(mockServiceTypeClientType1NullNetwork, never()).stopSendAndReceive(any());
// The stopDiscovery() is only used by MdnsSocketClient, which doesn't send
- // onAllSocketsDestroyed(). So the socket clients that send onAllSocketsDestroyed() do not
+ // onSocketDestroyed(). So the socket clients that send onSocketDestroyed() do not
// need to call stopDiscovery().
verify(socketClient, never()).stopDiscovery();
}
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 b812fa6..29de272 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -28,7 +28,6 @@
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;
@@ -67,18 +66,18 @@
@Mock private MdnsServiceBrowserListener mListener;
@Mock private MdnsSocketClientBase.Callback mCallback;
@Mock private SocketCreationCallback mSocketCreationCallback;
- @Mock private SocketKey mSocketKey;
private MdnsMultinetworkSocketClient mSocketClient;
private Handler mHandler;
+ private SocketKey mSocketKey;
@Before
public void setUp() throws SocketException {
MockitoAnnotations.initMocks(this);
- doReturn(mNetwork).when(mSocketKey).getNetwork();
final HandlerThread thread = new HandlerThread("MdnsMultinetworkSocketClientTest");
thread.start();
mHandler = new Handler(thread.getLooper());
+ mSocketKey = new SocketKey(1000 /* interfaceIndex */);
mSocketClient = new MdnsMultinetworkSocketClient(thread.getLooper(), mProvider);
mHandler.post(() -> mSocketClient.setCallback(mCallback));
}
@@ -125,10 +124,8 @@
doReturn(createEmptyNetworkInterface()).when(socket).getInterface();
}
- final SocketKey tetherSocketKey1 = mock(SocketKey.class);
- final SocketKey tetherSocketKey2 = mock(SocketKey.class);
- doReturn(null).when(tetherSocketKey1).getNetwork();
- doReturn(null).when(tetherSocketKey2).getNetwork();
+ final SocketKey tetherSocketKey1 = new SocketKey(1001 /* interfaceIndex */);
+ final SocketKey tetherSocketKey2 = new SocketKey(1002 /* interfaceIndex */);
// Notify socket created
callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
@@ -137,8 +134,8 @@
callback.onSocketCreated(tetherSocketKey2, tetherIfaceSock2, List.of());
verify(mSocketCreationCallback).onSocketCreated(tetherSocketKey2);
- // Send packet to IPv4 with target network and verify sending has been called.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork,
+ // Send packet to IPv4 with mSocketKey and verify sending has been called.
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mSocketKey,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet);
@@ -146,30 +143,30 @@
verify(tetherIfaceSock2, never()).send(any());
// Send packet to IPv4 with onlyUseIpv6OnIpv6OnlyNetworks = true, the packet will be sent.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork,
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mSocketKey,
true /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(2)).send(ipv4Packet);
verify(tetherIfaceSock1, never()).send(any());
verify(tetherIfaceSock2, never()).send(any());
- // Send packet to IPv6 without target network and verify sending has been called.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv6Packet, null,
+ // Send packet to IPv6 with tetherSocketKey1 and verify sending has been called.
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv6Packet, tetherSocketKey1,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, never()).send(ipv6Packet);
verify(tetherIfaceSock1).send(ipv6Packet);
- verify(tetherIfaceSock2).send(ipv6Packet);
+ verify(tetherIfaceSock2, never()).send(ipv6Packet);
// Send packet to IPv6 with onlyUseIpv6OnIpv6OnlyNetworks = true, the packet will not be
// sent. Therefore, the tetherIfaceSock1.send() and tetherIfaceSock2.send() are still be
// called once.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv6Packet, null,
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv6Packet, tetherSocketKey1,
true /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, never()).send(ipv6Packet);
verify(tetherIfaceSock1, times(1)).send(ipv6Packet);
- verify(tetherIfaceSock2, times(1)).send(ipv6Packet);
+ verify(tetherIfaceSock2, never()).send(ipv6Packet);
}
@Test
@@ -240,8 +237,8 @@
doReturn(createEmptyNetworkInterface()).when(socket2).getInterface();
doReturn(createEmptyNetworkInterface()).when(socket3).getInterface();
- final SocketKey socketKey2 = mock(SocketKey.class);
- final SocketKey socketKey3 = mock(SocketKey.class);
+ final SocketKey socketKey2 = new SocketKey(1001 /* interfaceIndex */);
+ final SocketKey socketKey3 = new SocketKey(1002 /* interfaceIndex */);
callback.onSocketCreated(mSocketKey, mSocket, List.of());
callback.onSocketCreated(socketKey2, socket2, List.of());
callback.onSocketCreated(socketKey3, socket3, List.of());
@@ -249,8 +246,8 @@
verify(mSocketCreationCallback).onSocketCreated(socketKey2);
verify(mSocketCreationCallback).onSocketCreated(socketKey3);
- // Send IPv4 packet on the non-null Network and verify sending has been called.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork,
+ // Send IPv4 packet on the mSocketKey and verify sending has been called.
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mSocketKey,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet);
@@ -278,43 +275,44 @@
verify(socketCreationCb2).onSocketCreated(socketKey2);
verify(socketCreationCb2).onSocketCreated(socketKey3);
- // Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null,
+ // Send IPv4 packet on socket2 and verify sending to the socket2 only.
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, socketKey2,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on
// mNetwork
verify(mSocket, times(1)).send(ipv4Packet);
verify(socket2).send(ipv4Packet);
- verify(socket3).send(ipv4Packet);
+ verify(socket3, never()).send(ipv4Packet);
// Unregister the second request
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2));
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2);
// Send IPv4 packet again and verify it's still sent a second time
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null,
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, socketKey2,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(socket2, times(2)).send(ipv4Packet);
- verify(socket3, times(2)).send(ipv4Packet);
+ verify(socket3, never()).send(ipv4Packet);
// Unrequest remaining sockets
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback);
// Send IPv4 packet and verify no more sending.
- mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null,
+ mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mSocketKey,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(1)).send(ipv4Packet);
verify(socket2, times(2)).send(ipv4Packet);
- verify(socket3, times(2)).send(ipv4Packet);
+ verify(socket3, never()).send(ipv4Packet);
}
@Test
public void testNotifyNetworkUnrequested_SocketsOnNullNetwork() {
final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketKey otherSocketKey = new SocketKey(1001 /* interfaceIndex */);
final SocketCallback callback = expectSocketCallback(
mListener, null /* requestedNetwork */);
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
@@ -322,33 +320,36 @@
callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
- callback.onSocketCreated(mSocketKey, otherSocket, List.of());
- verify(mSocketCreationCallback, times(2)).onSocketCreated(mSocketKey);
+ callback.onSocketCreated(otherSocketKey, otherSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(otherSocketKey);
- verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(mSocketKey);
+ verify(mSocketCreationCallback, never()).onSocketDestroyed(mSocketKey);
+ verify(mSocketCreationCallback, never()).onSocketDestroyed(otherSocketKey);
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mProvider).unrequestSocket(callback);
- verify(mSocketCreationCallback).onAllSocketsDestroyed(mSocketKey);
+ verify(mSocketCreationCallback).onSocketDestroyed(mSocketKey);
+ verify(mSocketCreationCallback).onSocketDestroyed(otherSocketKey);
}
@Test
public void testSocketCreatedAndDestroyed_NullNetwork() throws IOException {
final MdnsInterfaceSocket otherSocket = mock(MdnsInterfaceSocket.class);
+ final SocketKey otherSocketKey = new SocketKey(1001 /* interfaceIndex */);
final SocketCallback callback = expectSocketCallback(mListener, null /* network */);
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
- callback.onSocketCreated(mSocketKey, otherSocket, List.of());
- verify(mSocketCreationCallback, times(2)).onSocketCreated(mSocketKey);
+ callback.onSocketCreated(otherSocketKey, otherSocket, List.of());
+ verify(mSocketCreationCallback).onSocketCreated(otherSocketKey);
// Notify socket destroyed
callback.onInterfaceDestroyed(mSocketKey, mSocket);
- verifyNoMoreInteractions(mSocketCreationCallback);
- callback.onInterfaceDestroyed(mSocketKey, otherSocket);
- verify(mSocketCreationCallback).onAllSocketsDestroyed(mSocketKey);
+ verify(mSocketCreationCallback).onSocketDestroyed(mSocketKey);
+ callback.onInterfaceDestroyed(otherSocketKey, otherSocket);
+ verify(mSocketCreationCallback).onSocketDestroyed(otherSocketKey);
}
}
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 9892e9f..03e893f 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -438,7 +438,7 @@
client.processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), TEST_TTL), /* interfaceIndex= */ 20, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
verify(mockListenerOne).onServiceNameDiscovered(any());
verify(mockListenerOne).onServiceFound(any());
@@ -459,8 +459,7 @@
private static void verifyServiceInfo(MdnsServiceInfo serviceInfo, String serviceName,
String[] serviceType, List<String> ipv4Addresses, List<String> ipv6Addresses, int port,
- List<String> subTypes, Map<String, String> attributes, int interfaceIndex,
- Network network) {
+ List<String> subTypes, Map<String, String> attributes, SocketKey socketKey) {
assertEquals(serviceName, serviceInfo.getServiceInstanceName());
assertArrayEquals(serviceType, serviceInfo.getServiceType());
assertEquals(ipv4Addresses, serviceInfo.getIpv4Addresses());
@@ -471,8 +470,8 @@
assertTrue(attributes.containsKey(key));
assertEquals(attributes.get(key), serviceInfo.getAttributeByKey(key));
}
- assertEquals(interfaceIndex, serviceInfo.getInterfaceIndex());
- assertEquals(network, serviceInfo.getNetwork());
+ assertEquals(socketKey.getInterfaceIndex(), serviceInfo.getInterfaceIndex());
+ assertEquals(socketKey.getNetwork(), serviceInfo.getNetwork());
}
@Test
@@ -482,7 +481,7 @@
client.processResponse(createResponse(
"service-instance-1", null /* host */, 0 /* port */,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getAllValues().get(0),
"service-instance-1",
@@ -492,8 +491,7 @@
/* port= */ 0,
/* subTypes= */ List.of(),
Collections.emptyMap(),
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
@@ -508,14 +506,14 @@
client.processResponse(createResponse(
"service-instance-1", ipV4Address, 5353,
/* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), /* interfaceIndex= */ 20, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response with a different port and updated text attributes.
client.processResponse(createResponse(
"service-instance-1", ipV4Address, 5354,
/* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"), TEST_TTL),
- /* interfaceIndex= */ 20, mockNetwork);
+ socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
@@ -527,8 +525,7 @@
5353 /* port */,
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
- 20 /* interfaceIndex */,
- mockNetwork);
+ socketKey);
// Verify onServiceFound was called once for the initial response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -538,8 +535,8 @@
assertEquals(initialServiceInfo.getPort(), 5353);
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertNull(initialServiceInfo.getAttributeByKey("key"));
- assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
- assertEquals(mockNetwork, initialServiceInfo.getNetwork());
+ assertEquals(socketKey.getInterfaceIndex(), initialServiceInfo.getInterfaceIndex());
+ assertEquals(socketKey.getNetwork(), initialServiceInfo.getNetwork());
// Verify onServiceUpdated was called once for the second response.
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -550,8 +547,8 @@
assertTrue(updatedServiceInfo.hasSubtypes());
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
- assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
- assertEquals(mockNetwork, updatedServiceInfo.getNetwork());
+ assertEquals(socketKey.getInterfaceIndex(), updatedServiceInfo.getInterfaceIndex());
+ assertEquals(socketKey.getNetwork(), updatedServiceInfo.getNetwork());
}
@Test
@@ -563,14 +560,14 @@
client.processResponse(createResponse(
"service-instance-1", ipV6Address, 5353,
/* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), /* interfaceIndex= */ 20, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response with a different port and updated text attributes.
client.processResponse(createResponse(
"service-instance-1", ipV6Address, 5354,
/* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"), TEST_TTL),
- /* interfaceIndex= */ 20, mockNetwork);
+ socketKey);
// Verify onServiceNameDiscovered was called once for the initial response.
verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
@@ -582,8 +579,7 @@
5353 /* port */,
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
- 20 /* interfaceIndex */,
- mockNetwork);
+ socketKey);
// Verify onServiceFound was called once for the initial response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -593,8 +589,8 @@
assertEquals(initialServiceInfo.getPort(), 5353);
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertNull(initialServiceInfo.getAttributeByKey("key"));
- assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
- assertEquals(mockNetwork, initialServiceInfo.getNetwork());
+ assertEquals(socketKey.getInterfaceIndex(), initialServiceInfo.getInterfaceIndex());
+ assertEquals(socketKey.getNetwork(), initialServiceInfo.getNetwork());
// Verify onServiceUpdated was called once for the second response.
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -605,8 +601,8 @@
assertTrue(updatedServiceInfo.hasSubtypes());
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
- assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
- assertEquals(mockNetwork, updatedServiceInfo.getNetwork());
+ assertEquals(socketKey.getInterfaceIndex(), updatedServiceInfo.getInterfaceIndex());
+ assertEquals(socketKey.getNetwork(), updatedServiceInfo.getNetwork());
}
private void verifyServiceRemovedNoCallback(MdnsServiceBrowserListener listener) {
@@ -615,17 +611,17 @@
}
private void verifyServiceRemovedCallback(MdnsServiceBrowserListener listener,
- String serviceName, String[] serviceType, int interfaceIndex, Network network) {
+ String serviceName, String[] serviceType, SocketKey socketKey) {
verify(listener).onServiceRemoved(argThat(
info -> serviceName.equals(info.getServiceInstanceName())
&& Arrays.equals(serviceType, info.getServiceType())
- && info.getInterfaceIndex() == interfaceIndex
- && network.equals(info.getNetwork())));
+ && info.getInterfaceIndex() == socketKey.getInterfaceIndex()
+ && socketKey.getNetwork().equals(info.getNetwork())));
verify(listener).onServiceNameRemoved(argThat(
info -> serviceName.equals(info.getServiceInstanceName())
&& Arrays.equals(serviceType, info.getServiceType())
- && info.getInterfaceIndex() == interfaceIndex
- && network.equals(info.getNetwork())));
+ && info.getInterfaceIndex() == socketKey.getInterfaceIndex()
+ && socketKey.getNetwork().equals(info.getNetwork())));
}
@Test
@@ -639,12 +635,12 @@
client.processResponse(createResponse(
serviceName, ipV6Address, 5353,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
client.processResponse(createResponse(
"goodbye-service", ipV6Address, 5353,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), /* ptrTtlMillis= */ 0L), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), /* ptrTtlMillis= */ 0L), socketKey);
// Verify removed callback won't be called if the service is not existed.
verifyServiceRemovedNoCallback(mockListenerOne);
@@ -654,11 +650,11 @@
client.processResponse(createResponse(
serviceName, ipV6Address, 5353,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), 0L), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), 0L), socketKey);
verifyServiceRemovedCallback(
- mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, mockNetwork);
+ mockListenerOne, serviceName, SERVICE_TYPE_LABELS, socketKey);
verifyServiceRemovedCallback(
- mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, mockNetwork);
+ mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, socketKey);
}
@Test
@@ -667,7 +663,7 @@
client.processResponse(createResponse(
"service-instance-1", "192.168.1.1", 5353,
/* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
@@ -681,8 +677,7 @@
5353 /* port */,
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
// Verify onServiceFound was called once for the existing response.
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -697,7 +692,7 @@
client.processResponse(createResponse(
"service-instance-1", "192.168.1.1", 5353,
SERVICE_TYPE_LABELS,
- Collections.emptyMap(), /* ptrTtlMillis= */ 0L), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), /* ptrTtlMillis= */ 0L), socketKey);
client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
@@ -727,7 +722,7 @@
// Process the initial response.
client.processResponse(createResponse(
serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
currentThreadExecutor.getAndClearLastScheduledRunnable();
@@ -744,8 +739,8 @@
firstMdnsTask.run();
// Verify removed callback was called.
- verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
- INTERFACE_INDEX, mockNetwork);
+ verifyServiceRemovedCallback(
+ mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, socketKey);
}
@Test
@@ -766,7 +761,7 @@
// Process the initial response.
client.processResponse(createResponse(
serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
currentThreadExecutor.getAndClearLastScheduledRunnable();
@@ -799,7 +794,7 @@
// Process the initial response.
client.processResponse(createResponse(
serviceInstanceName, "192.168.1.1", 5353, /* subtype= */ "ABCDE",
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Clear the scheduled runnable.
currentThreadExecutor.getAndClearLastScheduledRunnable();
@@ -809,8 +804,8 @@
firstMdnsTask.run();
// Verify removed callback was called.
- verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
- INTERFACE_INDEX, mockNetwork);
+ verifyServiceRemovedCallback(
+ mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, socketKey);
}
@Test
@@ -825,23 +820,23 @@
final String subtype = "ABCDE";
client.processResponse(createResponse(
serviceName, null, 5353, subtype,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Process a second response which has ip address to make response become complete.
client.processResponse(createResponse(
serviceName, ipV4Address, 5353, subtype,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Process a third response with a different ip address, port and updated text attributes.
client.processResponse(createResponse(
serviceName, ipV6Address, 5354, subtype,
- Collections.singletonMap("key", "value"), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.singletonMap("key", "value"), TEST_TTL), socketKey);
// Process the last response which is goodbye message (with the main type, not subtype).
client.processResponse(createResponse(
serviceName, ipV6Address, 5354, SERVICE_TYPE_LABELS,
Collections.singletonMap("key", "value"), /* ptrTtlMillis= */ 0L),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
// Verify onServiceNameDiscovered was first called for the initial response.
inOrder.verify(mockListenerOne).onServiceNameDiscovered(serviceInfoCaptor.capture());
@@ -853,8 +848,7 @@
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
// Verify onServiceFound was second called for the second response.
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
@@ -866,8 +860,7 @@
5353 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", null) /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
// Verify onServiceUpdated was third called for the third response.
inOrder.verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
@@ -879,8 +872,7 @@
5354 /* port */,
Collections.singletonList(subtype) /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
// Verify onServiceRemoved was called for the last response.
inOrder.verify(mockListenerOne).onServiceRemoved(serviceInfoCaptor.capture());
@@ -892,8 +884,7 @@
5354 /* port */,
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
// Verify onServiceNameRemoved was called for the last response.
inOrder.verify(mockListenerOne).onServiceNameRemoved(serviceInfoCaptor.capture());
@@ -905,8 +896,7 @@
5354 /* port */,
Collections.singletonList("ABCDE") /* subTypes */,
Collections.singletonMap("key", "value") /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
}
@Test
@@ -932,7 +922,7 @@
// Send twice for IPv4 and IPv6
inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse(
srvTxtQueryCaptor.capture(),
- eq(mockNetwork), eq(false));
+ eq(socketKey), eq(false));
final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
@@ -955,7 +945,7 @@
Collections.emptyList() /* authorityRecords */,
Collections.emptyList() /* additionalRecords */);
- client.processResponse(srvTxtResponse, INTERFACE_INDEX, mockNetwork);
+ client.processResponse(srvTxtResponse, socketKey);
// Expect a query for A/AAAA
final ArgumentCaptor<DatagramPacket> addressQueryCaptor =
@@ -963,7 +953,7 @@
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse(
addressQueryCaptor.capture(),
- eq(mockNetwork), eq(false));
+ eq(socketKey), eq(false));
final MdnsPacket addressQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(addressQueryCaptor.getValue()));
@@ -985,7 +975,7 @@
Collections.emptyList() /* additionalRecords */);
inOrder.verify(mockListenerOne, never()).onServiceNameDiscovered(any());
- client.processResponse(addressResponse, INTERFACE_INDEX, mockNetwork);
+ client.processResponse(addressResponse, socketKey);
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
verifyServiceInfo(serviceInfoCaptor.getValue(),
@@ -996,8 +986,7 @@
1234 /* port */,
Collections.emptyList() /* subTypes */,
Collections.emptyMap() /* attributes */,
- INTERFACE_INDEX,
- mockNetwork);
+ socketKey);
}
@Test
@@ -1023,7 +1012,7 @@
// Send twice for IPv4 and IPv6
inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse(
srvTxtQueryCaptor.capture(),
- eq(mockNetwork), eq(false));
+ eq(socketKey), eq(false));
final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
@@ -1050,7 +1039,7 @@
InetAddresses.parseNumericAddress(ipV6Address))),
Collections.emptyList() /* authorityRecords */,
Collections.emptyList() /* additionalRecords */);
- client.processResponse(srvTxtResponse, INTERFACE_INDEX, mockNetwork);
+ client.processResponse(srvTxtResponse, socketKey);
inOrder.verify(mockListenerOne).onServiceNameDiscovered(any());
inOrder.verify(mockListenerOne).onServiceFound(any());
@@ -1069,7 +1058,7 @@
// Second and later sends are sent as "expect multicast response" queries
inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse(
renewalQueryCaptor.capture(),
- eq(mockNetwork), eq(false));
+ eq(socketKey), eq(false));
inOrder.verify(mockListenerOne).onDiscoveryQuerySent(any(), anyInt());
final MdnsPacket renewalPacket = MdnsPacket.parse(
new MdnsPacketReader(renewalQueryCaptor.getValue()));
@@ -1095,7 +1084,7 @@
InetAddresses.parseNumericAddress(ipV6Address))),
Collections.emptyList() /* authorityRecords */,
Collections.emptyList() /* additionalRecords */);
- client.processResponse(refreshedSrvTxtResponse, INTERFACE_INDEX, mockNetwork);
+ client.processResponse(refreshedSrvTxtResponse, socketKey);
// Advance time to updatedReceiptTime + 1, expected no refresh query because the cache
// should contain the record that have update last receipt time.
@@ -1126,23 +1115,23 @@
client.processResponse(createResponse(
requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
// Complete response from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
// Address update from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Goodbye from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
- Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), 0L /* ttl */), socketKey);
// mockListenerOne gets notified for the requested instance
verify(mockListenerOne).onServiceNameDiscovered(
@@ -1207,23 +1196,23 @@
newAnswers,
packetWithoutSubtype.authorityRecords,
packetWithoutSubtype.additionalRecords);
- client.processResponse(packetWithSubtype, INTERFACE_INDEX, mockNetwork);
+ client.processResponse(packetWithSubtype, socketKey);
// Complete response from otherInstanceName, without subtype
client.processResponse(createResponse(
otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
// Address update from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
- Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), TEST_TTL), socketKey);
// Goodbye from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
- Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
+ Collections.emptyMap(), 0L /* ttl */), socketKey);
// mockListenerOne gets notified for the requested instance
final ArgumentMatcher<MdnsServiceInfo> subtypeInstanceMatcher = info ->
@@ -1278,13 +1267,13 @@
client.processResponse(createResponse(
requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
// Complete response from otherInstanceName
client.processResponse(createResponse(
otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL),
- INTERFACE_INDEX, mockNetwork);
+ socketKey);
verify(expectedSendFutures[1], never()).cancel(true);
client.notifySocketDestroyed();
@@ -1332,17 +1321,17 @@
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
if (expectsUnicastResponse) {
verify(mockSocketClient).sendPacketRequestingUnicastResponse(
- expectedIPv4Packets[index], mockNetwork, false);
+ expectedIPv4Packets[index], socketKey, false);
if (multipleSocketDiscovery) {
verify(mockSocketClient).sendPacketRequestingUnicastResponse(
- expectedIPv6Packets[index], mockNetwork, false);
+ expectedIPv6Packets[index], socketKey, false);
}
} else {
verify(mockSocketClient).sendPacketRequestingMulticastResponse(
- expectedIPv4Packets[index], mockNetwork, false);
+ expectedIPv4Packets[index], socketKey, false);
if (multipleSocketDiscovery) {
verify(mockSocketClient).sendPacketRequestingMulticastResponse(
- expectedIPv6Packets[index], mockNetwork, false);
+ expectedIPv6Packets[index], socketKey, false);
}
}
}
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 0eac5ec..e971de7 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -157,7 +157,6 @@
TETHERED_IFACE_NAME);
doReturn(789).when(mDeps).getNetworkInterfaceIndexByName(
WIFI_P2P_IFACE_NAME);
- doReturn(TETHERED_IFACE_IDX).when(mDeps).getInterfaceIndex(any());
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start();
mHandler = new Handler(thread.getLooper());