From 9637e84eedddb3c87dd688a607a17765a8fe2197 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Fri, 2 Jun 2023 13:38:29 +0900 Subject: [PATCH] Adds option to control whethert to send IPv6 packet on IPv6 only network Add options to control whether only send IPv6 packet on IPv6 only network. In some use case, IPv6 query should not be sent on a dual network. Added a small improvement to send an ANY query if both SRV & PTR records are needed. Bug: 284480254 Test: atest CtsNetTest FrameworksNetTests Change-Id: I8f55db6f5e8ae606ac167fb363bc9b2fb9b5fc14 --- .../mdns/EnqueueMdnsQueryCallable.java | 107 ++++++++---------- .../mdns/MdnsMultinetworkSocketClient.java | 60 +++++++--- .../connectivity/mdns/MdnsSearchOptions.java | 48 ++++++-- .../mdns/MdnsServiceTypeClient.java | 13 ++- .../server/connectivity/mdns/MdnsSocket.java | 8 ++ .../connectivity/mdns/MdnsSocketClient.java | 57 +++++----- .../mdns/MdnsSocketClientBase.java | 14 +-- .../MdnsMultinetworkSocketClientTest.java | 36 +++++- .../mdns/MdnsServiceTypeClientTests.java | 51 +++++---- .../mdns/MdnsSocketClientTests.java | 45 +++++--- 10 files changed, 276 insertions(+), 163 deletions(-) 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 84faf12036..2d5bb001f8 100644 --- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java +++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java @@ -29,7 +29,6 @@ import com.android.server.connectivity.mdns.util.MdnsUtils; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.DatagramPacket; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; @@ -78,6 +77,7 @@ public class EnqueueMdnsQueryCallable implements Callable servicesToResolve; @NonNull private final MdnsResponseDecoder.Clock clock; + private final boolean onlyUseIpv6OnIpv6OnlyNetworks; EnqueueMdnsQueryCallable( @NonNull MdnsSocketClientBase requestSender, @@ -87,6 +87,7 @@ public class EnqueueMdnsQueryCallable implements Callable servicesToResolve, @NonNull MdnsResponseDecoder.Clock clock) { @@ -97,6 +98,7 @@ public class EnqueueMdnsQueryCallable implements Callable(servicesToResolve); this.clock = clock; @@ -128,24 +130,29 @@ public class EnqueueMdnsQueryCallable implements Callable(serviceName, MdnsRecord.TYPE_TXT)); - } - if (!response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded( - response.getServiceRecord(), now)) { - missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV)); - // The hostname is not yet known, so queries for address records will be sent - // the next time the EnqueueMdnsQueryCallable is enqueued if the reply does not - // contain them. In practice, advertisers should include the address records - // when queried for SRV, although it's not a MUST requirement (RFC6763 12.2). - // TODO: Figure out how to renew the A/AAAA record. Usually A/AAAA record will - // be included in the response to the SRV record so in high chances there is - // no need to renew them individually. - } else if (!response.hasInet4AddressRecord() && !response.hasInet6AddressRecord()) { - final String[] host = response.getServiceRecord().getServiceHost(); - missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_A)); - missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_AAAA)); + boolean renewTxt = !response.hasTextRecord() || MdnsUtils.isRecordRenewalNeeded( + response.getTextRecord(), now); + boolean renewSrv = !response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded( + response.getServiceRecord(), now); + if (renewSrv && renewTxt) { + missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_ANY)); + } else { + if (renewTxt) { + missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_TXT)); + } + if (renewSrv) { + missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV)); + // The hostname is not yet known, so queries for address records will be + // sent the next time the EnqueueMdnsQueryCallable is enqueued if the reply + // does not contain them. In practice, advertisers should include the + // address records when queried for SRV, although it's not a MUST + // requirement (RFC6763 12.2). + } else if (!response.hasInet4AddressRecord() + && !response.hasInet6AddressRecord()) { + final String[] host = response.getServiceRecord().getServiceHost(); + missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_A)); + missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_AAAA)); + } } } numQuestions += missingKnownAnswerRecords.size(); @@ -183,24 +190,9 @@ public class EnqueueMdnsQueryCallable implements Callable activeSockets = getActiveSockets(); + boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlyNetworks( + activeSockets, targetNetwork); for (int i = 0; i < activeSockets.size(); i++) { final MdnsInterfaceSocket socket = activeSockets.keyAt(i); final Network network = activeSockets.valueAt(i).getNetwork(); // Check ip capability and network before sending packet - if (((isIpv6 && socket.hasJoinedIpv6()) || (isIpv4 && socket.hasJoinedIpv4())) + 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). @@ -237,6 +241,19 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase { } } + private boolean isIpv6OnlyNetworks( + @NonNull ArrayMap activeSockets, + @Nullable Network targetNetwork) { + 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()) { + return false; + } + } + return true; + } + private void processResponsePacket(byte[] recvbuf, int length, @NonNull SocketKey socketKey) { int packetNumber = ++mReceivedPacketNumber; @@ -259,21 +276,38 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase { } /** - * Sends a mDNS request packet via given network that asks for multicast response. Null network - * means sending packet via all networks. + * Send a mDNS request packet via given network that asks for multicast response. + * + *

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 sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet, + @Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) { + mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks)); + } + @Override - public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - mHandler.post(() -> sendMdnsPacket(packet, network)); + public void sendPacketRequestingMulticastResponse( + @NonNull DatagramPacket packet, boolean onlyUseIpv6OnIpv6OnlyNetworks) { + sendPacketRequestingMulticastResponse( + packet, null /* network */, onlyUseIpv6OnIpv6OnlyNetworks); } /** - * Sends a mDNS request packet via given network that asks for unicast response. Null network - * means sending packet via all networks. + * Send a mDNS request packet via given network that asks for unicast response. + * + *

The socket client may use a null network to identify some or all interfaces, in which case + * passing null sends the packet to these. */ - @Override - public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - // TODO: Separate unicast packet. - mHandler.post(() -> sendMdnsPacket(packet, network)); + public void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet, + @Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) { + mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks)); } -} + + @Override + public void sendPacketRequestingUnicastResponse( + @NonNull DatagramPacket packet, boolean onlyUseIpv6OnIpv6OnlyNetworks) { + sendPacketRequestingUnicastResponse( + packet, null /* network */, onlyUseIpv6OnIpv6OnlyNetworks); + } +} \ No newline at end of file diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java index 3da6bd0105..98c80ee7f1 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSearchOptions.java @@ -44,10 +44,13 @@ public class MdnsSearchOptions implements Parcelable { new Parcelable.Creator() { @Override public MdnsSearchOptions createFromParcel(Parcel source) { - return new MdnsSearchOptions(source.createStringArrayList(), - source.readBoolean(), source.readBoolean(), + return new MdnsSearchOptions( + source.createStringArrayList(), + source.readBoolean(), + source.readBoolean(), source.readParcelable(null), - source.readString()); + source.readString(), + (source.dataAvail() > 0) ? source.readBoolean() : false); } @Override @@ -61,18 +64,25 @@ public class MdnsSearchOptions implements Parcelable { private final String resolveInstanceName; private final boolean isPassiveMode; + private final boolean onlyUseIpv6OnIpv6OnlyNetworks; private final boolean removeExpiredService; // The target network for searching. Null network means search on all possible interfaces. @Nullable private final Network mNetwork; /** Parcelable constructs for a {@link MdnsSearchOptions}. */ - MdnsSearchOptions(List subtypes, boolean isPassiveMode, boolean removeExpiredService, - @Nullable Network network, @Nullable String resolveInstanceName) { + MdnsSearchOptions( + List subtypes, + boolean isPassiveMode, + boolean removeExpiredService, + @Nullable Network network, + @Nullable String resolveInstanceName, + boolean onlyUseIpv6OnIpv6OnlyNetworks) { this.subtypes = new ArrayList<>(); if (subtypes != null) { this.subtypes.addAll(subtypes); } this.isPassiveMode = isPassiveMode; + this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; this.removeExpiredService = removeExpiredService; mNetwork = network; this.resolveInstanceName = resolveInstanceName; @@ -104,6 +114,14 @@ public class MdnsSearchOptions implements Parcelable { return isPassiveMode; } + /** + * @return {@code true} if only the IPv4 mDNS host should be queried on network that supports + * both IPv6 as well as IPv4. On an IPv6-only network, this is ignored. + */ + public boolean onlyUseIpv6OnIpv6OnlyNetworks() { + return onlyUseIpv6OnIpv6OnlyNetworks; + } + /** Returns {@code true} if service will be removed after its TTL expires. */ public boolean removeExpiredService() { return removeExpiredService; @@ -140,12 +158,14 @@ public class MdnsSearchOptions implements Parcelable { out.writeBoolean(removeExpiredService); out.writeParcelable(mNetwork, 0); out.writeString(resolveInstanceName); + out.writeBoolean(onlyUseIpv6OnIpv6OnlyNetworks); } /** A builder to create {@link MdnsSearchOptions}. */ public static final class Builder { private final Set subtypes; private boolean isPassiveMode = true; + private boolean onlyUseIpv6OnIpv6OnlyNetworks = false; private boolean removeExpiredService; private Network mNetwork; private String resolveInstanceName; @@ -189,6 +209,15 @@ public class MdnsSearchOptions implements Parcelable { return this; } + /** + * Sets if only the IPv4 mDNS host should be queried on a network that is both IPv4 & IPv6. + * On an IPv6-only network, this is ignored. + */ + public Builder setOnlyUseIpv6OnIpv6OnlyNetworks(boolean onlyUseIpv6OnIpv6OnlyNetworks) { + this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; + return this; + } + /** * Sets if the service should be removed after TTL. * @@ -223,8 +252,13 @@ public class MdnsSearchOptions implements Parcelable { /** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */ public MdnsSearchOptions build() { - return new MdnsSearchOptions(new ArrayList<>(subtypes), isPassiveMode, - removeExpiredService, mNetwork, resolveInstanceName); + return new MdnsSearchOptions( + new ArrayList<>(subtypes), + isPassiveMode, + removeExpiredService, + mNetwork, + resolveInstanceName, + onlyUseIpv6OnIpv6OnlyNetworks); } } } \ 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 bdc673e1df..a36eb1bb0f 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java @@ -198,6 +198,7 @@ public class MdnsServiceTypeClient { final QueryTaskConfig taskConfig = new QueryTaskConfig( searchOptions.getSubtypes(), searchOptions.isPassiveMode(), + searchOptions.onlyUseIpv6OnIpv6OnlyNetworks(), currentSessionId, socketKey); if (hadReply) { @@ -220,7 +221,7 @@ public class MdnsServiceTypeClient { final boolean matchesInstanceName = options.getResolveInstanceName() == null // DNS is case-insensitive, so ignore case in the comparison || MdnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(), - response.getServiceInstanceName()); + response.getServiceInstanceName()); // If discovery is requiring some subtypes, the response must have one that matches a // requested one. @@ -427,6 +428,7 @@ public class MdnsServiceTypeClient { private final boolean alwaysAskForUnicastResponse = MdnsConfigs.alwaysAskForUnicastResponseInEachBurst(); private final boolean usePassiveMode; + private final boolean onlyUseIpv6OnIpv6OnlyNetworks; private final long sessionId; @VisibleForTesting int transactionId; @@ -439,9 +441,13 @@ public class MdnsServiceTypeClient { private boolean isFirstBurst; @NonNull private final SocketKey socketKey; - QueryTaskConfig(@NonNull Collection subtypes, boolean usePassiveMode, - long sessionId, @NonNull SocketKey socketKey) { + QueryTaskConfig(@NonNull Collection subtypes, + boolean usePassiveMode, + boolean onlyUseIpv6OnIpv6OnlyNetworks, + long sessionId, + @Nullable SocketKey socketKey) { this.usePassiveMode = usePassiveMode; + this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; this.subtypes = new ArrayList<>(subtypes); this.queriesPerBurst = QUERIES_PER_BURST; this.burstCounter = 0; @@ -559,6 +565,7 @@ public class MdnsServiceTypeClient { config.expectUnicastResponse, config.transactionId, config.socketKey.getNetwork(), + config.onlyUseIpv6OnIpv6OnlyNetworks, sendDiscoveryQueries, servicesToResolve, clock) diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java index 5fd13548f4..cdd9f76199 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocket.java @@ -93,6 +93,10 @@ public class MdnsSocket { } for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { multicastSocket.joinGroup(multicastAddress, networkInterface.getNetworkInterface()); + if (!isOnIPv6OnlyNetwork) { + multicastSocket.joinGroup( + MULTICAST_IPV6_ADDRESS, networkInterface.getNetworkInterface()); + } } } @@ -105,6 +109,10 @@ public class MdnsSocket { } for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { multicastSocket.leaveGroup(multicastAddress, networkInterface.getNetworkInterface()); + if (!isOnIPv6OnlyNetwork) { + multicastSocket.leaveGroup( + MULTICAST_IPV6_ADDRESS, networkInterface.getNetworkInterface()); + } } } diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java index 2b6e5d0d35..9c9812d0ed 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java @@ -31,6 +31,9 @@ import com.android.server.connectivity.mdns.util.MdnsLogger; import java.io.IOException; import java.net.DatagramPacket; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetSocketAddress; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; @@ -194,38 +197,22 @@ public class MdnsSocketClient implements MdnsSocketClientBase { } } - /** Sends a mDNS request packet that asks for multicast response. */ - public void sendMulticastPacket(@NonNull DatagramPacket packet) { - sendMdnsPacket(packet, multicastPacketQueue); + @Override + public void sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet, + boolean onlyUseIpv6OnIpv6OnlyNetworks) { + sendMdnsPacket(packet, multicastPacketQueue, onlyUseIpv6OnIpv6OnlyNetworks); } - /** Sends a mDNS request packet that asks for unicast response. */ - public void sendUnicastPacket(DatagramPacket packet) { + @Override + public void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet, + boolean onlyUseIpv6OnIpv6OnlyNetworks) { if (useSeparateSocketForUnicast) { - sendMdnsPacket(packet, unicastPacketQueue); + sendMdnsPacket(packet, unicastPacketQueue, onlyUseIpv6OnIpv6OnlyNetworks); } else { - sendMdnsPacket(packet, multicastPacketQueue); + sendMdnsPacket(packet, multicastPacketQueue, onlyUseIpv6OnIpv6OnlyNetworks); } } - @Override - public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - if (network != null) { - throw new IllegalArgumentException("This socket client does not support sending to " - + "specific networks"); - } - sendMulticastPacket(packet); - } - - @Override - public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - if (network != null) { - throw new IllegalArgumentException("This socket client does not support sending to " - + "specific networks"); - } - sendUnicastPacket(packet); - } - @Override public void notifyNetworkRequested( @NonNull MdnsServiceBrowserListener listener, @@ -243,11 +230,25 @@ public class MdnsSocketClient implements MdnsSocketClientBase { return false; } - private void sendMdnsPacket(DatagramPacket packet, Queue packetQueueToUse) { + private void sendMdnsPacket(DatagramPacket packet, Queue packetQueueToUse, + boolean onlyUseIpv6OnIpv6OnlyNetworks) { if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) { LOGGER.w("sendMdnsPacket() is called after discovery already stopped"); return; } + + final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress() + instanceof Inet4Address; + final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress() + instanceof Inet6Address; + final boolean ipv6Only = multicastSocket != null && multicastSocket.isOnIPv6OnlyNetwork(); + if (isIpv4 && ipv6Only) { + return; + } + if (isIpv6 && !ipv6Only && onlyUseIpv6OnIpv6OnlyNetworks) { + return; + } + synchronized (packetQueueToUse) { while (packetQueueToUse.size() >= MdnsConfigs.mdnsPacketQueueMaxSize()) { packetQueueToUse.remove(); @@ -535,8 +536,4 @@ public class MdnsSocketClient implements MdnsSocketClientBase { } packets.clear(); } - - public boolean isOnIPv6OnlyNetwork() { - return multicastSocket != null && multicastSocket.isOnIPv6OnlyNetwork(); - } } \ No newline at end of file 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 a35925a9f9..5e4a8b58f7 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java @@ -41,19 +41,15 @@ public interface MdnsSocketClientBase { /** * Send a mDNS request packet via given network that asks for multicast response. - * - *

The socket client may use a null network to identify some or all interfaces, in which case - * passing null sends the packet to these. */ - void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network); + void sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet, + boolean onlyUseIpv6OnIpv6OnlyNetworks); /** * Send a mDNS request packet via given network that asks for unicast response. - * - *

The socket client may use a null network to identify some or all interfaces, in which case - * passing null sends the packet to these. */ - void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network); + void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet, + boolean onlyUseIpv6OnIpv6OnlyNetworks); /*** Notify that the given network is requested for mdns discovery / resolution */ void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener, @@ -88,4 +84,4 @@ public interface MdnsSocketClientBase { /*** Notify requested socket is destroyed */ void onAllSocketsDestroyed(@NonNull SocketKey socketKey); } -} +} \ No newline at end of file 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 f7ef0778cb..b812fa6b70 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java @@ -138,18 +138,38 @@ public class MdnsMultinetworkSocketClientTest { verify(mSocketCreationCallback).onSocketCreated(tetherSocketKey2); // Send packet to IPv4 with target network and verify sending has been called. - mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(mSocket).send(ipv4Packet); verify(tetherIfaceSock1, never()).send(any()); verify(tetherIfaceSock2, never()).send(any()); + // Send packet to IPv4 with onlyUseIpv6OnIpv6OnlyNetworks = true, the packet will be sent. + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork, + 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.sendMulticastPacket(ipv6Packet, null); + mSocketClient.sendPacketRequestingMulticastResponse(ipv6Packet, null, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(mSocket, never()).send(ipv6Packet); verify(tetherIfaceSock1).send(ipv6Packet); verify(tetherIfaceSock2).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, + true /* onlyUseIpv6OnIpv6OnlyNetworks */); + HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); + verify(mSocket, never()).send(ipv6Packet); + verify(tetherIfaceSock1, times(1)).send(ipv6Packet); + verify(tetherIfaceSock2, times(1)).send(ipv6Packet); } @Test @@ -230,7 +250,8 @@ public class MdnsMultinetworkSocketClientTest { verify(mSocketCreationCallback).onSocketCreated(socketKey3); // Send IPv4 packet on the non-null Network and verify sending has been called. - mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, mNetwork, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(mSocket).send(ipv4Packet); verify(socket2, never()).send(any()); @@ -258,7 +279,8 @@ public class MdnsMultinetworkSocketClientTest { verify(socketCreationCb2).onSocketCreated(socketKey3); // Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets. - mSocketClient.sendMulticastPacket(ipv4Packet, null); + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); // ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on // mNetwork @@ -271,7 +293,8 @@ public class MdnsMultinetworkSocketClientTest { verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2); // Send IPv4 packet again and verify it's still sent a second time - mSocketClient.sendMulticastPacket(ipv4Packet, null); + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(socket2, times(2)).send(ipv4Packet); verify(socket3, times(2)).send(ipv4Packet); @@ -281,7 +304,8 @@ public class MdnsMultinetworkSocketClientTest { verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback); // Send IPv4 packet and verify no more sending. - mSocketClient.sendMulticastPacket(ipv4Packet, null); + mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(mSocket, times(1)).send(ipv4Packet); verify(socket2, times(2)).send(ipv4Packet); 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 635a1d4240..9892e9fad8 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java @@ -327,7 +327,8 @@ public class MdnsServiceTypeClientTests { MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build(); QueryTaskConfig config = new QueryTaskConfig( - searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1, socketKey); + searchOptions.getSubtypes(), searchOptions.isPassiveMode(), + false /* onlyUseIpv6OnIpv6OnlyNetworks */, 1, socketKey); // This is the first query. We will ask for unicast response. assertTrue(config.expectUnicastResponse); @@ -356,7 +357,8 @@ public class MdnsServiceTypeClientTests { MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build(); QueryTaskConfig config = new QueryTaskConfig( - searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 1, socketKey); + searchOptions.getSubtypes(), searchOptions.isPassiveMode(), + false /* onlyUseIpv6OnIpv6OnlyNetworks */, 1, socketKey); // This is the first query. We will ask for unicast response. assertTrue(config.expectUnicastResponse); @@ -928,16 +930,16 @@ public class MdnsServiceTypeClientTests { ArgumentCaptor.forClass(DatagramPacket.class); currentThreadExecutor.getAndClearLastScheduledRunnable().run(); // Send twice for IPv4 and IPv6 - inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(), - eq(mockNetwork)); + inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse( + srvTxtQueryCaptor.capture(), + eq(mockNetwork), eq(false)); final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse( new MdnsPacketReader(srvTxtQueryCaptor.getValue())); final String[] serviceName = getTestServiceName(instanceName); assertFalse(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_PTR)); - assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName)); - assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName)); + assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_ANY, serviceName)); // Process a response with SRV+TXT final MdnsPacket srvTxtResponse = new MdnsPacket( @@ -959,8 +961,9 @@ public class MdnsServiceTypeClientTests { final ArgumentCaptor addressQueryCaptor = ArgumentCaptor.forClass(DatagramPacket.class); currentThreadExecutor.getAndClearLastScheduledRunnable().run(); - inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(addressQueryCaptor.capture(), - eq(mockNetwork)); + inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse( + addressQueryCaptor.capture(), + eq(mockNetwork), eq(false)); final MdnsPacket addressQueryPacket = MdnsPacket.parse( new MdnsPacketReader(addressQueryCaptor.getValue())); @@ -1018,15 +1021,15 @@ public class MdnsServiceTypeClientTests { ArgumentCaptor.forClass(DatagramPacket.class); currentThreadExecutor.getAndClearLastScheduledRunnable().run(); // Send twice for IPv4 and IPv6 - inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(), - eq(mockNetwork)); + inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse( + srvTxtQueryCaptor.capture(), + eq(mockNetwork), eq(false)); final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse( new MdnsPacketReader(srvTxtQueryCaptor.getValue())); final String[] serviceName = getTestServiceName(instanceName); - assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName)); - assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName)); + assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_ANY, serviceName)); // Process a response with all records final MdnsPacket srvTxtResponse = new MdnsPacket( @@ -1064,13 +1067,13 @@ public class MdnsServiceTypeClientTests { final ArgumentCaptor renewalQueryCaptor = ArgumentCaptor.forClass(DatagramPacket.class); // Second and later sends are sent as "expect multicast response" queries - inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(renewalQueryCaptor.capture(), - eq(mockNetwork)); + inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse( + renewalQueryCaptor.capture(), + eq(mockNetwork), eq(false)); inOrder.verify(mockListenerOne).onDiscoveryQuerySent(any(), anyInt()); final MdnsPacket renewalPacket = MdnsPacket.parse( new MdnsPacketReader(renewalQueryCaptor.getValue())); - assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_SRV, serviceName)); - assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_TXT, serviceName)); + assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_ANY, serviceName)); inOrder.verifyNoMoreInteractions(); long updatedReceiptTime = TEST_ELAPSED_REALTIME + TEST_TTL; @@ -1328,18 +1331,18 @@ public class MdnsServiceTypeClientTests { assertEquals(currentThreadExecutor.getAndClearLastScheduledDelayInMs(), timeInMs); currentThreadExecutor.getAndClearLastScheduledRunnable().run(); if (expectsUnicastResponse) { - verify(mockSocketClient).sendUnicastPacket( - expectedIPv4Packets[index], mockNetwork); + verify(mockSocketClient).sendPacketRequestingUnicastResponse( + expectedIPv4Packets[index], mockNetwork, false); if (multipleSocketDiscovery) { - verify(mockSocketClient).sendUnicastPacket( - expectedIPv6Packets[index], mockNetwork); + verify(mockSocketClient).sendPacketRequestingUnicastResponse( + expectedIPv6Packets[index], mockNetwork, false); } } else { - verify(mockSocketClient).sendMulticastPacket( - expectedIPv4Packets[index], mockNetwork); + verify(mockSocketClient).sendPacketRequestingMulticastResponse( + expectedIPv4Packets[index], mockNetwork, false); if (multipleSocketDiscovery) { - verify(mockSocketClient).sendMulticastPacket( - expectedIPv6Packets[index], mockNetwork); + verify(mockSocketClient).sendPacketRequestingMulticastResponse( + expectedIPv6Packets[index], mockNetwork, false); } } } diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java index e30c2496d5..69efc61431 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketClientTests.java @@ -54,6 +54,7 @@ import org.mockito.invocation.InvocationOnMock; import java.io.IOException; import java.net.DatagramPacket; +import java.net.InetSocketAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -221,15 +222,17 @@ public class MdnsSocketClientTests { assertTrue(unicastReceiverThread.isAlive()); // Sends a packet. - DatagramPacket packet = new DatagramPacket(buf, 0, 5); - mdnsClient.sendMulticastPacket(packet); + DatagramPacket packet = getTestDatagramPacket(); + mdnsClient.sendPacketRequestingMulticastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); // mockMulticastSocket.send() will be called on another thread. If we verify it immediately, // it may not be called yet. So timeout is added. verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); // Verify the packet is sent by the unicast socket. - mdnsClient.sendUnicastPacket(packet); + mdnsClient.sendPacketRequestingUnicastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(1)).send(packet); @@ -272,15 +275,17 @@ public class MdnsSocketClientTests { assertNull(unicastReceiverThread); // Sends a packet. - DatagramPacket packet = new DatagramPacket(buf, 0, 5); - mdnsClient.sendMulticastPacket(packet); + DatagramPacket packet = getTestDatagramPacket(); + mdnsClient.sendPacketRequestingMulticastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); // mockMulticastSocket.send() will be called on another thread. If we verify it immediately, // it may not be called yet. So timeout is added. verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); // Verify the packet is sent by the multicast socket as well. - mdnsClient.sendUnicastPacket(packet); + mdnsClient.sendPacketRequestingUnicastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); verify(mockMulticastSocket, timeout(TIMEOUT).times(2)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); @@ -332,7 +337,8 @@ public class MdnsSocketClientTests { public void testStopDiscovery_queueIsCleared() throws IOException { mdnsClient.startDiscovery(); mdnsClient.stopDiscovery(); - mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); + mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(), + false /* onlyUseIpv6OnIpv6OnlyNetworks */); synchronized (mdnsClient.multicastPacketQueue) { assertTrue(mdnsClient.multicastPacketQueue.isEmpty()); @@ -343,7 +349,8 @@ public class MdnsSocketClientTests { public void testSendPacket_afterDiscoveryStops() throws IOException { mdnsClient.startDiscovery(); mdnsClient.stopDiscovery(); - mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); + mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(), + false /* onlyUseIpv6OnIpv6OnlyNetworks */); synchronized (mdnsClient.multicastPacketQueue) { assertTrue(mdnsClient.multicastPacketQueue.isEmpty()); @@ -356,7 +363,8 @@ public class MdnsSocketClientTests { //MdnsConfigsFlagsImpl.mdnsPacketQueueMaxSize.override(2L); mdnsClient.startDiscovery(); for (int i = 0; i < 100; i++) { - mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); + mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(), + false /* onlyUseIpv6OnIpv6OnlyNetworks */); } synchronized (mdnsClient.multicastPacketQueue) { @@ -452,9 +460,11 @@ public class MdnsSocketClientTests { enableUnicastResponse.set(true); mdnsClient.startDiscovery(); - DatagramPacket packet = new DatagramPacket(buf, 0, 5); - mdnsClient.sendUnicastPacket(packet); - mdnsClient.sendMulticastPacket(packet); + DatagramPacket packet = getTestDatagramPacket(); + mdnsClient.sendPacketRequestingUnicastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); + mdnsClient.sendPacketRequestingMulticastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); // Wait for the timer to be triggered. Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2); @@ -484,8 +494,10 @@ public class MdnsSocketClientTests { assertFalse(mdnsClient.receivedUnicastResponse); assertFalse(mdnsClient.cannotReceiveMulticastResponse.get()); - mdnsClient.sendUnicastPacket(packet); - mdnsClient.sendMulticastPacket(packet); + mdnsClient.sendPacketRequestingUnicastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); + mdnsClient.sendPacketRequestingMulticastResponse(packet, + false /* onlyUseIpv6OnIpv6OnlyNetworks */); Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2); // Verify cannotReceiveMulticastResponse is not set the true because we didn't receive the @@ -540,4 +552,9 @@ public class MdnsSocketClientTests { verify(mockCallback, timeout(TIMEOUT).atLeast(1)) .onResponseReceived(any(), argThat(key -> key.getInterfaceIndex() == -1)); } + + private DatagramPacket getTestDatagramPacket() { + return new DatagramPacket(buf, 0, 5, + new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), 5353 /* port */)); + } } \ No newline at end of file