Merge "Adds option to control whethert to send IPv6 packet on IPv6 only network" am: 454c06e956 am: 882e74b042

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2613049

Change-Id: I7506d242c693deae1539629a93f95f188286c430
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot
2023-06-17 11:00:18 +00:00
committed by Automerger Merge Worker
10 changed files with 276 additions and 163 deletions

View File

@@ -29,7 +29,6 @@ import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -78,6 +77,7 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
private final List<MdnsResponse> servicesToResolve; private final List<MdnsResponse> servicesToResolve;
@NonNull @NonNull
private final MdnsResponseDecoder.Clock clock; private final MdnsResponseDecoder.Clock clock;
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
EnqueueMdnsQueryCallable( EnqueueMdnsQueryCallable(
@NonNull MdnsSocketClientBase requestSender, @NonNull MdnsSocketClientBase requestSender,
@@ -87,6 +87,7 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
boolean expectUnicastResponse, boolean expectUnicastResponse,
int transactionId, int transactionId,
@Nullable Network network, @Nullable Network network,
boolean onlyUseIpv6OnIpv6OnlyNetworks,
boolean sendDiscoveryQueries, boolean sendDiscoveryQueries,
@NonNull Collection<MdnsResponse> servicesToResolve, @NonNull Collection<MdnsResponse> servicesToResolve,
@NonNull MdnsResponseDecoder.Clock clock) { @NonNull MdnsResponseDecoder.Clock clock) {
@@ -97,6 +98,7 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
this.expectUnicastResponse = expectUnicastResponse; this.expectUnicastResponse = expectUnicastResponse;
this.transactionId = transactionId; this.transactionId = transactionId;
this.network = network; this.network = network;
this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
this.sendDiscoveryQueries = sendDiscoveryQueries; this.sendDiscoveryQueries = sendDiscoveryQueries;
this.servicesToResolve = new ArrayList<>(servicesToResolve); this.servicesToResolve = new ArrayList<>(servicesToResolve);
this.clock = clock; this.clock = clock;
@@ -128,24 +130,29 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
for (MdnsResponse response : servicesToResolve) { for (MdnsResponse response : servicesToResolve) {
final String[] serviceName = response.getServiceName(); final String[] serviceName = response.getServiceName();
if (serviceName == null) continue; if (serviceName == null) continue;
if (!response.hasTextRecord() || MdnsUtils.isRecordRenewalNeeded( boolean renewTxt = !response.hasTextRecord() || MdnsUtils.isRecordRenewalNeeded(
response.getTextRecord(), now)) { response.getTextRecord(), now);
missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_TXT)); boolean renewSrv = !response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded(
} response.getServiceRecord(), now);
if (!response.hasServiceRecord() || MdnsUtils.isRecordRenewalNeeded( if (renewSrv && renewTxt) {
response.getServiceRecord(), now)) { missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_ANY));
missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV)); } else {
// The hostname is not yet known, so queries for address records will be sent if (renewTxt) {
// the next time the EnqueueMdnsQueryCallable is enqueued if the reply does not missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_TXT));
// contain them. In practice, advertisers should include the address records }
// when queried for SRV, although it's not a MUST requirement (RFC6763 12.2). if (renewSrv) {
// TODO: Figure out how to renew the A/AAAA record. Usually A/AAAA record will missingKnownAnswerRecords.add(new Pair<>(serviceName, MdnsRecord.TYPE_SRV));
// be included in the response to the SRV record so in high chances there is // The hostname is not yet known, so queries for address records will be
// no need to renew them individually. // sent the next time the EnqueueMdnsQueryCallable is enqueued if the reply
} else if (!response.hasInet4AddressRecord() && !response.hasInet6AddressRecord()) { // does not contain them. In practice, advertisers should include the
final String[] host = response.getServiceRecord().getServiceHost(); // address records when queried for SRV, although it's not a MUST
missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_A)); // requirement (RFC6763 12.2).
missingKnownAnswerRecords.add(new Pair<>(host, MdnsRecord.TYPE_AAAA)); } 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(); numQuestions += missingKnownAnswerRecords.size();
@@ -183,24 +190,9 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
writeQuestion(serviceTypeLabels, MdnsRecord.TYPE_PTR); writeQuestion(serviceTypeLabels, MdnsRecord.TYPE_PTR);
} }
if (requestSender instanceof MdnsMultinetworkSocketClient) { sendPacketToIpv4AndIpv6(requestSender, MdnsConstants.MDNS_PORT);
sendPacketToIpv4AndIpv6(requestSender, MdnsConstants.MDNS_PORT, network); for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
for (Integer emulatorPort : castShellEmulatorMdnsPorts) { sendPacketToIpv4AndIpv6(requestSender, emulatorPort);
sendPacketToIpv4AndIpv6(requestSender, emulatorPort, network);
}
} else if (requestSender instanceof MdnsSocketClient) {
final MdnsSocketClient client = (MdnsSocketClient) requestSender;
InetAddress mdnsAddress = MdnsConstants.getMdnsIPv4Address();
if (client.isOnIPv6OnlyNetwork()) {
mdnsAddress = MdnsConstants.getMdnsIPv6Address();
}
sendPacketTo(client, new InetSocketAddress(mdnsAddress, MdnsConstants.MDNS_PORT));
for (Integer emulatorPort : castShellEmulatorMdnsPorts) {
sendPacketTo(client, new InetSocketAddress(mdnsAddress, emulatorPort));
}
} else {
throw new IOException("Unknown socket client type: " + requestSender.getClass());
} }
return Pair.create(transactionId, subtypes); return Pair.create(transactionId, subtypes);
} catch (IOException e) { } catch (IOException e) {
@@ -218,38 +210,39 @@ public class EnqueueMdnsQueryCallable implements Callable<Pair<Integer, List<Str
| (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0)); | (expectUnicastResponse ? MdnsConstants.QCLASS_UNICAST : 0));
} }
private void sendPacketTo(MdnsSocketClient requestSender, InetSocketAddress address) private void sendPacket(MdnsSocketClientBase requestSender, InetSocketAddress address)
throws IOException { throws IOException {
DatagramPacket packet = packetWriter.getPacket(address); DatagramPacket packet = packetWriter.getPacket(address);
if (expectUnicastResponse) { if (expectUnicastResponse) {
requestSender.sendUnicastPacket(packet); if (requestSender instanceof MdnsMultinetworkSocketClient) {
((MdnsMultinetworkSocketClient) requestSender).sendPacketRequestingUnicastResponse(
packet, network, onlyUseIpv6OnIpv6OnlyNetworks);
} else {
requestSender.sendPacketRequestingUnicastResponse(
packet, onlyUseIpv6OnIpv6OnlyNetworks);
}
} else { } else {
requestSender.sendMulticastPacket(packet); if (requestSender instanceof MdnsMultinetworkSocketClient) {
((MdnsMultinetworkSocketClient) requestSender)
.sendPacketRequestingMulticastResponse(
packet, network, onlyUseIpv6OnIpv6OnlyNetworks);
} else {
requestSender.sendPacketRequestingMulticastResponse(
packet, onlyUseIpv6OnIpv6OnlyNetworks);
}
} }
} }
private void sendPacketFromNetwork(MdnsSocketClientBase requestSender, private void sendPacketToIpv4AndIpv6(MdnsSocketClientBase requestSender, int port) {
InetSocketAddress address, Network network)
throws IOException {
DatagramPacket packet = packetWriter.getPacket(address);
if (expectUnicastResponse) {
requestSender.sendUnicastPacket(packet, network);
} else {
requestSender.sendMulticastPacket(packet, network);
}
}
private void sendPacketToIpv4AndIpv6(MdnsSocketClientBase requestSender, int port,
Network network) {
try { try {
sendPacketFromNetwork(requestSender, sendPacket(requestSender,
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), port), network); new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), port));
} catch (IOException e) { } catch (IOException e) {
Log.i(TAG, "Can't send packet to IPv4", e); Log.i(TAG, "Can't send packet to IPv4", e);
} }
try { try {
sendPacketFromNetwork(requestSender, sendPacket(requestSender,
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), port), network); new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), port));
} catch (IOException e) { } catch (IOException e) {
Log.i(TAG, "Can't send packet to IPv6", e); Log.i(TAG, "Can't send packet to IPv6", e);
} }

View File

@@ -213,17 +213,21 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
return true; return true;
} }
private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork) { private void sendMdnsPacket(@NonNull DatagramPacket packet, @Nullable Network targetNetwork,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress() final boolean isIpv6 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet6Address; instanceof Inet6Address;
final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress() final boolean isIpv4 = ((InetSocketAddress) packet.getSocketAddress()).getAddress()
instanceof Inet4Address; instanceof Inet4Address;
final ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets = getActiveSockets(); final ArrayMap<MdnsInterfaceSocket, SocketKey> activeSockets = getActiveSockets();
boolean shouldQueryIpv6 = !onlyUseIpv6OnIpv6OnlyNetworks || isIpv6OnlyNetworks(
activeSockets, targetNetwork);
for (int i = 0; i < activeSockets.size(); i++) { for (int i = 0; i < activeSockets.size(); i++) {
final MdnsInterfaceSocket socket = activeSockets.keyAt(i); final MdnsInterfaceSocket socket = activeSockets.keyAt(i);
final Network network = activeSockets.valueAt(i).getNetwork(); final Network network = activeSockets.valueAt(i).getNetwork();
// Check ip capability and network before sending packet // 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 // Contrary to MdnsUtils.isNetworkMatched, only send packets targeting
// the null network to interfaces that have the null network (tethering // the null network to interfaces that have the null network (tethering
// downstream interfaces). // downstream interfaces).
@@ -237,6 +241,19 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
} }
} }
private boolean isIpv6OnlyNetworks(
@NonNull ArrayMap<MdnsInterfaceSocket, SocketKey> 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) { private void processResponsePacket(byte[] recvbuf, int length, @NonNull SocketKey socketKey) {
int packetNumber = ++mReceivedPacketNumber; 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 * Send a mDNS request packet via given network that asks for multicast response.
* means sending packet via all networks. *
* <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 sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet,
@Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks));
}
@Override @Override
public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { public void sendPacketRequestingMulticastResponse(
mHandler.post(() -> sendMdnsPacket(packet, network)); @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 * Send a mDNS request packet via given network that asks for unicast response.
* means sending packet via all networks. *
* <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.
*/ */
@Override public void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet,
public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { @Nullable Network network, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
// TODO: Separate unicast packet. mHandler.post(() -> sendMdnsPacket(packet, network, onlyUseIpv6OnIpv6OnlyNetworks));
mHandler.post(() -> sendMdnsPacket(packet, network));
} }
}
@Override
public void sendPacketRequestingUnicastResponse(
@NonNull DatagramPacket packet, boolean onlyUseIpv6OnIpv6OnlyNetworks) {
sendPacketRequestingUnicastResponse(
packet, null /* network */, onlyUseIpv6OnIpv6OnlyNetworks);
}
}

View File

@@ -44,10 +44,13 @@ public class MdnsSearchOptions implements Parcelable {
new Parcelable.Creator<MdnsSearchOptions>() { new Parcelable.Creator<MdnsSearchOptions>() {
@Override @Override
public MdnsSearchOptions createFromParcel(Parcel source) { public MdnsSearchOptions createFromParcel(Parcel source) {
return new MdnsSearchOptions(source.createStringArrayList(), return new MdnsSearchOptions(
source.readBoolean(), source.readBoolean(), source.createStringArrayList(),
source.readBoolean(),
source.readBoolean(),
source.readParcelable(null), source.readParcelable(null),
source.readString()); source.readString(),
(source.dataAvail() > 0) ? source.readBoolean() : false);
} }
@Override @Override
@@ -61,18 +64,25 @@ public class MdnsSearchOptions implements Parcelable {
private final String resolveInstanceName; private final String resolveInstanceName;
private final boolean isPassiveMode; private final boolean isPassiveMode;
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
private final boolean removeExpiredService; private final boolean removeExpiredService;
// The target network for searching. Null network means search on all possible interfaces. // The target network for searching. Null network means search on all possible interfaces.
@Nullable private final Network mNetwork; @Nullable private final Network mNetwork;
/** Parcelable constructs for a {@link MdnsSearchOptions}. */ /** Parcelable constructs for a {@link MdnsSearchOptions}. */
MdnsSearchOptions(List<String> subtypes, boolean isPassiveMode, boolean removeExpiredService, MdnsSearchOptions(
@Nullable Network network, @Nullable String resolveInstanceName) { List<String> subtypes,
boolean isPassiveMode,
boolean removeExpiredService,
@Nullable Network network,
@Nullable String resolveInstanceName,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
this.subtypes = new ArrayList<>(); this.subtypes = new ArrayList<>();
if (subtypes != null) { if (subtypes != null) {
this.subtypes.addAll(subtypes); this.subtypes.addAll(subtypes);
} }
this.isPassiveMode = isPassiveMode; this.isPassiveMode = isPassiveMode;
this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
this.removeExpiredService = removeExpiredService; this.removeExpiredService = removeExpiredService;
mNetwork = network; mNetwork = network;
this.resolveInstanceName = resolveInstanceName; this.resolveInstanceName = resolveInstanceName;
@@ -104,6 +114,14 @@ public class MdnsSearchOptions implements Parcelable {
return isPassiveMode; 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. */ /** Returns {@code true} if service will be removed after its TTL expires. */
public boolean removeExpiredService() { public boolean removeExpiredService() {
return removeExpiredService; return removeExpiredService;
@@ -140,12 +158,14 @@ public class MdnsSearchOptions implements Parcelable {
out.writeBoolean(removeExpiredService); out.writeBoolean(removeExpiredService);
out.writeParcelable(mNetwork, 0); out.writeParcelable(mNetwork, 0);
out.writeString(resolveInstanceName); out.writeString(resolveInstanceName);
out.writeBoolean(onlyUseIpv6OnIpv6OnlyNetworks);
} }
/** A builder to create {@link MdnsSearchOptions}. */ /** A builder to create {@link MdnsSearchOptions}. */
public static final class Builder { public static final class Builder {
private final Set<String> subtypes; private final Set<String> subtypes;
private boolean isPassiveMode = true; private boolean isPassiveMode = true;
private boolean onlyUseIpv6OnIpv6OnlyNetworks = false;
private boolean removeExpiredService; private boolean removeExpiredService;
private Network mNetwork; private Network mNetwork;
private String resolveInstanceName; private String resolveInstanceName;
@@ -189,6 +209,15 @@ public class MdnsSearchOptions implements Parcelable {
return this; 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. * 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. */ /** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */
public MdnsSearchOptions build() { public MdnsSearchOptions build() {
return new MdnsSearchOptions(new ArrayList<>(subtypes), isPassiveMode, return new MdnsSearchOptions(
removeExpiredService, mNetwork, resolveInstanceName); new ArrayList<>(subtypes),
isPassiveMode,
removeExpiredService,
mNetwork,
resolveInstanceName,
onlyUseIpv6OnIpv6OnlyNetworks);
} }
} }
} }

View File

@@ -198,6 +198,7 @@ public class MdnsServiceTypeClient {
final QueryTaskConfig taskConfig = new QueryTaskConfig( final QueryTaskConfig taskConfig = new QueryTaskConfig(
searchOptions.getSubtypes(), searchOptions.getSubtypes(),
searchOptions.isPassiveMode(), searchOptions.isPassiveMode(),
searchOptions.onlyUseIpv6OnIpv6OnlyNetworks(),
currentSessionId, currentSessionId,
socketKey); socketKey);
if (hadReply) { if (hadReply) {
@@ -220,7 +221,7 @@ public class MdnsServiceTypeClient {
final boolean matchesInstanceName = options.getResolveInstanceName() == null final boolean matchesInstanceName = options.getResolveInstanceName() == null
// DNS is case-insensitive, so ignore case in the comparison // DNS is case-insensitive, so ignore case in the comparison
|| MdnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(), || MdnsUtils.equalsIgnoreDnsCase(options.getResolveInstanceName(),
response.getServiceInstanceName()); response.getServiceInstanceName());
// If discovery is requiring some subtypes, the response must have one that matches a // If discovery is requiring some subtypes, the response must have one that matches a
// requested one. // requested one.
@@ -427,6 +428,7 @@ public class MdnsServiceTypeClient {
private final boolean alwaysAskForUnicastResponse = private final boolean alwaysAskForUnicastResponse =
MdnsConfigs.alwaysAskForUnicastResponseInEachBurst(); MdnsConfigs.alwaysAskForUnicastResponseInEachBurst();
private final boolean usePassiveMode; private final boolean usePassiveMode;
private final boolean onlyUseIpv6OnIpv6OnlyNetworks;
private final long sessionId; private final long sessionId;
@VisibleForTesting @VisibleForTesting
int transactionId; int transactionId;
@@ -439,9 +441,13 @@ public class MdnsServiceTypeClient {
private boolean isFirstBurst; private boolean isFirstBurst;
@NonNull private final SocketKey socketKey; @NonNull private final SocketKey socketKey;
QueryTaskConfig(@NonNull Collection<String> subtypes, boolean usePassiveMode, QueryTaskConfig(@NonNull Collection<String> subtypes,
long sessionId, @NonNull SocketKey socketKey) { boolean usePassiveMode,
boolean onlyUseIpv6OnIpv6OnlyNetworks,
long sessionId,
@Nullable SocketKey socketKey) {
this.usePassiveMode = usePassiveMode; this.usePassiveMode = usePassiveMode;
this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks;
this.subtypes = new ArrayList<>(subtypes); this.subtypes = new ArrayList<>(subtypes);
this.queriesPerBurst = QUERIES_PER_BURST; this.queriesPerBurst = QUERIES_PER_BURST;
this.burstCounter = 0; this.burstCounter = 0;
@@ -559,6 +565,7 @@ public class MdnsServiceTypeClient {
config.expectUnicastResponse, config.expectUnicastResponse,
config.transactionId, config.transactionId,
config.socketKey.getNetwork(), config.socketKey.getNetwork(),
config.onlyUseIpv6OnIpv6OnlyNetworks,
sendDiscoveryQueries, sendDiscoveryQueries,
servicesToResolve, servicesToResolve,
clock) clock)

View File

@@ -93,6 +93,10 @@ public class MdnsSocket {
} }
for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { for (NetworkInterfaceWrapper networkInterface : networkInterfaces) {
multicastSocket.joinGroup(multicastAddress, networkInterface.getNetworkInterface()); 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) { for (NetworkInterfaceWrapper networkInterface : networkInterfaces) {
multicastSocket.leaveGroup(multicastAddress, networkInterface.getNetworkInterface()); multicastSocket.leaveGroup(multicastAddress, networkInterface.getNetworkInterface());
if (!isOnIPv6OnlyNetwork) {
multicastSocket.leaveGroup(
MULTICAST_IPV6_ADDRESS, networkInterface.getNetworkInterface());
}
} }
} }

View File

@@ -31,6 +31,9 @@ import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -194,38 +197,22 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
} }
} }
/** Sends a mDNS request packet that asks for multicast response. */ @Override
public void sendMulticastPacket(@NonNull DatagramPacket packet) { public void sendPacketRequestingMulticastResponse(@NonNull DatagramPacket packet,
sendMdnsPacket(packet, multicastPacketQueue); boolean onlyUseIpv6OnIpv6OnlyNetworks) {
sendMdnsPacket(packet, multicastPacketQueue, onlyUseIpv6OnIpv6OnlyNetworks);
} }
/** Sends a mDNS request packet that asks for unicast response. */ @Override
public void sendUnicastPacket(DatagramPacket packet) { public void sendPacketRequestingUnicastResponse(@NonNull DatagramPacket packet,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
if (useSeparateSocketForUnicast) { if (useSeparateSocketForUnicast) {
sendMdnsPacket(packet, unicastPacketQueue); sendMdnsPacket(packet, unicastPacketQueue, onlyUseIpv6OnIpv6OnlyNetworks);
} else { } 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 @Override
public void notifyNetworkRequested( public void notifyNetworkRequested(
@NonNull MdnsServiceBrowserListener listener, @NonNull MdnsServiceBrowserListener listener,
@@ -243,11 +230,25 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
return false; return false;
} }
private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse) { private void sendMdnsPacket(DatagramPacket packet, Queue<DatagramPacket> packetQueueToUse,
boolean onlyUseIpv6OnIpv6OnlyNetworks) {
if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) { if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) {
LOGGER.w("sendMdnsPacket() is called after discovery already stopped"); LOGGER.w("sendMdnsPacket() is called after discovery already stopped");
return; 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) { synchronized (packetQueueToUse) {
while (packetQueueToUse.size() >= MdnsConfigs.mdnsPacketQueueMaxSize()) { while (packetQueueToUse.size() >= MdnsConfigs.mdnsPacketQueueMaxSize()) {
packetQueueToUse.remove(); packetQueueToUse.remove();
@@ -535,8 +536,4 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
} }
packets.clear(); packets.clear();
} }
public boolean isOnIPv6OnlyNetwork() {
return multicastSocket != null && multicastSocket.isOnIPv6OnlyNetwork();
}
} }

View File

@@ -41,19 +41,15 @@ public interface MdnsSocketClientBase {
/** /**
* Send a mDNS request packet via given network that asks for multicast response. * 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.
*/ */
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. * Send a mDNS request packet via given network 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.
*/ */
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 */ /*** Notify that the given network is requested for mdns discovery / resolution */
void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener, void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener,
@@ -88,4 +84,4 @@ public interface MdnsSocketClientBase {
/*** Notify requested socket is destroyed */ /*** Notify requested socket is destroyed */
void onAllSocketsDestroyed(@NonNull SocketKey socketKey); void onAllSocketsDestroyed(@NonNull SocketKey socketKey);
} }
} }

View File

@@ -138,18 +138,38 @@ public class MdnsMultinetworkSocketClientTest {
verify(mSocketCreationCallback).onSocketCreated(tetherSocketKey2); verify(mSocketCreationCallback).onSocketCreated(tetherSocketKey2);
// Send packet to IPv4 with target network and verify sending has been called. // 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); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet); verify(mSocket).send(ipv4Packet);
verify(tetherIfaceSock1, never()).send(any()); verify(tetherIfaceSock1, never()).send(any());
verify(tetherIfaceSock2, 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. // 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); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, never()).send(ipv6Packet); verify(mSocket, never()).send(ipv6Packet);
verify(tetherIfaceSock1).send(ipv6Packet); verify(tetherIfaceSock1).send(ipv6Packet);
verify(tetherIfaceSock2).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 @Test
@@ -230,7 +250,8 @@ public class MdnsMultinetworkSocketClientTest {
verify(mSocketCreationCallback).onSocketCreated(socketKey3); verify(mSocketCreationCallback).onSocketCreated(socketKey3);
// Send IPv4 packet on the non-null Network and verify sending has been called. // 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); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket).send(ipv4Packet); verify(mSocket).send(ipv4Packet);
verify(socket2, never()).send(any()); verify(socket2, never()).send(any());
@@ -258,7 +279,8 @@ public class MdnsMultinetworkSocketClientTest {
verify(socketCreationCb2).onSocketCreated(socketKey3); verify(socketCreationCb2).onSocketCreated(socketKey3);
// Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets. // 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); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on // ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on
// mNetwork // mNetwork
@@ -271,7 +293,8 @@ public class MdnsMultinetworkSocketClientTest {
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2); verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2);
// Send IPv4 packet again and verify it's still sent a second time // 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); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(socket2, times(2)).send(ipv4Packet); verify(socket2, times(2)).send(ipv4Packet);
verify(socket3, times(2)).send(ipv4Packet); verify(socket3, times(2)).send(ipv4Packet);
@@ -281,7 +304,8 @@ public class MdnsMultinetworkSocketClientTest {
verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback); verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback);
// Send IPv4 packet and verify no more sending. // Send IPv4 packet and verify no more sending.
mSocketClient.sendMulticastPacket(ipv4Packet, null); mSocketClient.sendPacketRequestingMulticastResponse(ipv4Packet, null,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mSocket, times(1)).send(ipv4Packet); verify(mSocket, times(1)).send(ipv4Packet);
verify(socket2, times(2)).send(ipv4Packet); verify(socket2, times(2)).send(ipv4Packet);

View File

@@ -327,7 +327,8 @@ public class MdnsServiceTypeClientTests {
MdnsSearchOptions searchOptions = MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build(); MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig( 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. // This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse); assertTrue(config.expectUnicastResponse);
@@ -356,7 +357,8 @@ public class MdnsServiceTypeClientTests {
MdnsSearchOptions searchOptions = MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build(); MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig( 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. // This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse); assertTrue(config.expectUnicastResponse);
@@ -928,16 +930,16 @@ public class MdnsServiceTypeClientTests {
ArgumentCaptor.forClass(DatagramPacket.class); ArgumentCaptor.forClass(DatagramPacket.class);
currentThreadExecutor.getAndClearLastScheduledRunnable().run(); currentThreadExecutor.getAndClearLastScheduledRunnable().run();
// Send twice for IPv4 and IPv6 // Send twice for IPv4 and IPv6
inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(), inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse(
eq(mockNetwork)); srvTxtQueryCaptor.capture(),
eq(mockNetwork), eq(false));
final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse( final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(srvTxtQueryCaptor.getValue())); new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
final String[] serviceName = getTestServiceName(instanceName); final String[] serviceName = getTestServiceName(instanceName);
assertFalse(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_PTR)); assertFalse(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_PTR));
assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName)); assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_ANY, serviceName));
assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName));
// Process a response with SRV+TXT // Process a response with SRV+TXT
final MdnsPacket srvTxtResponse = new MdnsPacket( final MdnsPacket srvTxtResponse = new MdnsPacket(
@@ -959,8 +961,9 @@ public class MdnsServiceTypeClientTests {
final ArgumentCaptor<DatagramPacket> addressQueryCaptor = final ArgumentCaptor<DatagramPacket> addressQueryCaptor =
ArgumentCaptor.forClass(DatagramPacket.class); ArgumentCaptor.forClass(DatagramPacket.class);
currentThreadExecutor.getAndClearLastScheduledRunnable().run(); currentThreadExecutor.getAndClearLastScheduledRunnable().run();
inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(addressQueryCaptor.capture(), inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse(
eq(mockNetwork)); addressQueryCaptor.capture(),
eq(mockNetwork), eq(false));
final MdnsPacket addressQueryPacket = MdnsPacket.parse( final MdnsPacket addressQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(addressQueryCaptor.getValue())); new MdnsPacketReader(addressQueryCaptor.getValue()));
@@ -1018,15 +1021,15 @@ public class MdnsServiceTypeClientTests {
ArgumentCaptor.forClass(DatagramPacket.class); ArgumentCaptor.forClass(DatagramPacket.class);
currentThreadExecutor.getAndClearLastScheduledRunnable().run(); currentThreadExecutor.getAndClearLastScheduledRunnable().run();
// Send twice for IPv4 and IPv6 // Send twice for IPv4 and IPv6
inOrder.verify(mockSocketClient, times(2)).sendUnicastPacket(srvTxtQueryCaptor.capture(), inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingUnicastResponse(
eq(mockNetwork)); srvTxtQueryCaptor.capture(),
eq(mockNetwork), eq(false));
final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse( final MdnsPacket srvTxtQueryPacket = MdnsPacket.parse(
new MdnsPacketReader(srvTxtQueryCaptor.getValue())); new MdnsPacketReader(srvTxtQueryCaptor.getValue()));
final String[] serviceName = getTestServiceName(instanceName); final String[] serviceName = getTestServiceName(instanceName);
assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_SRV, serviceName)); assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_ANY, serviceName));
assertTrue(hasQuestion(srvTxtQueryPacket, MdnsRecord.TYPE_TXT, serviceName));
// Process a response with all records // Process a response with all records
final MdnsPacket srvTxtResponse = new MdnsPacket( final MdnsPacket srvTxtResponse = new MdnsPacket(
@@ -1064,13 +1067,13 @@ public class MdnsServiceTypeClientTests {
final ArgumentCaptor<DatagramPacket> renewalQueryCaptor = final ArgumentCaptor<DatagramPacket> renewalQueryCaptor =
ArgumentCaptor.forClass(DatagramPacket.class); ArgumentCaptor.forClass(DatagramPacket.class);
// Second and later sends are sent as "expect multicast response" queries // Second and later sends are sent as "expect multicast response" queries
inOrder.verify(mockSocketClient, times(2)).sendMulticastPacket(renewalQueryCaptor.capture(), inOrder.verify(mockSocketClient, times(2)).sendPacketRequestingMulticastResponse(
eq(mockNetwork)); renewalQueryCaptor.capture(),
eq(mockNetwork), eq(false));
inOrder.verify(mockListenerOne).onDiscoveryQuerySent(any(), anyInt()); inOrder.verify(mockListenerOne).onDiscoveryQuerySent(any(), anyInt());
final MdnsPacket renewalPacket = MdnsPacket.parse( final MdnsPacket renewalPacket = MdnsPacket.parse(
new MdnsPacketReader(renewalQueryCaptor.getValue())); new MdnsPacketReader(renewalQueryCaptor.getValue()));
assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_SRV, serviceName)); assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_ANY, serviceName));
assertTrue(hasQuestion(renewalPacket, MdnsRecord.TYPE_TXT, serviceName));
inOrder.verifyNoMoreInteractions(); inOrder.verifyNoMoreInteractions();
long updatedReceiptTime = TEST_ELAPSED_REALTIME + TEST_TTL; long updatedReceiptTime = TEST_ELAPSED_REALTIME + TEST_TTL;
@@ -1328,18 +1331,18 @@ public class MdnsServiceTypeClientTests {
assertEquals(currentThreadExecutor.getAndClearLastScheduledDelayInMs(), timeInMs); assertEquals(currentThreadExecutor.getAndClearLastScheduledDelayInMs(), timeInMs);
currentThreadExecutor.getAndClearLastScheduledRunnable().run(); currentThreadExecutor.getAndClearLastScheduledRunnable().run();
if (expectsUnicastResponse) { if (expectsUnicastResponse) {
verify(mockSocketClient).sendUnicastPacket( verify(mockSocketClient).sendPacketRequestingUnicastResponse(
expectedIPv4Packets[index], mockNetwork); expectedIPv4Packets[index], mockNetwork, false);
if (multipleSocketDiscovery) { if (multipleSocketDiscovery) {
verify(mockSocketClient).sendUnicastPacket( verify(mockSocketClient).sendPacketRequestingUnicastResponse(
expectedIPv6Packets[index], mockNetwork); expectedIPv6Packets[index], mockNetwork, false);
} }
} else { } else {
verify(mockSocketClient).sendMulticastPacket( verify(mockSocketClient).sendPacketRequestingMulticastResponse(
expectedIPv4Packets[index], mockNetwork); expectedIPv4Packets[index], mockNetwork, false);
if (multipleSocketDiscovery) { if (multipleSocketDiscovery) {
verify(mockSocketClient).sendMulticastPacket( verify(mockSocketClient).sendPacketRequestingMulticastResponse(
expectedIPv6Packets[index], mockNetwork); expectedIPv6Packets[index], mockNetwork, false);
} }
} }
} }

View File

@@ -54,6 +54,7 @@ import org.mockito.invocation.InvocationOnMock;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -221,15 +222,17 @@ public class MdnsSocketClientTests {
assertTrue(unicastReceiverThread.isAlive()); assertTrue(unicastReceiverThread.isAlive());
// Sends a packet. // Sends a packet.
DatagramPacket packet = new DatagramPacket(buf, 0, 5); DatagramPacket packet = getTestDatagramPacket();
mdnsClient.sendMulticastPacket(packet); mdnsClient.sendPacketRequestingMulticastResponse(packet,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
// mockMulticastSocket.send() will be called on another thread. If we verify it immediately, // mockMulticastSocket.send() will be called on another thread. If we verify it immediately,
// it may not be called yet. So timeout is added. // it may not be called yet. So timeout is added.
verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet);
verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet);
// Verify the packet is sent by the unicast socket. // 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(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet);
verify(mockUnicastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(1)).send(packet);
@@ -272,15 +275,17 @@ public class MdnsSocketClientTests {
assertNull(unicastReceiverThread); assertNull(unicastReceiverThread);
// Sends a packet. // Sends a packet.
DatagramPacket packet = new DatagramPacket(buf, 0, 5); DatagramPacket packet = getTestDatagramPacket();
mdnsClient.sendMulticastPacket(packet); mdnsClient.sendPacketRequestingMulticastResponse(packet,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
// mockMulticastSocket.send() will be called on another thread. If we verify it immediately, // mockMulticastSocket.send() will be called on another thread. If we verify it immediately,
// it may not be called yet. So timeout is added. // it may not be called yet. So timeout is added.
verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet); verify(mockMulticastSocket, timeout(TIMEOUT).times(1)).send(packet);
verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet);
// Verify the packet is sent by the multicast socket as well. // 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(mockMulticastSocket, timeout(TIMEOUT).times(2)).send(packet);
verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet); verify(mockUnicastSocket, timeout(TIMEOUT).times(0)).send(packet);
@@ -332,7 +337,8 @@ public class MdnsSocketClientTests {
public void testStopDiscovery_queueIsCleared() throws IOException { public void testStopDiscovery_queueIsCleared() throws IOException {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
mdnsClient.stopDiscovery(); mdnsClient.stopDiscovery();
mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
synchronized (mdnsClient.multicastPacketQueue) { synchronized (mdnsClient.multicastPacketQueue) {
assertTrue(mdnsClient.multicastPacketQueue.isEmpty()); assertTrue(mdnsClient.multicastPacketQueue.isEmpty());
@@ -343,7 +349,8 @@ public class MdnsSocketClientTests {
public void testSendPacket_afterDiscoveryStops() throws IOException { public void testSendPacket_afterDiscoveryStops() throws IOException {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
mdnsClient.stopDiscovery(); mdnsClient.stopDiscovery();
mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
synchronized (mdnsClient.multicastPacketQueue) { synchronized (mdnsClient.multicastPacketQueue) {
assertTrue(mdnsClient.multicastPacketQueue.isEmpty()); assertTrue(mdnsClient.multicastPacketQueue.isEmpty());
@@ -356,7 +363,8 @@ public class MdnsSocketClientTests {
//MdnsConfigsFlagsImpl.mdnsPacketQueueMaxSize.override(2L); //MdnsConfigsFlagsImpl.mdnsPacketQueueMaxSize.override(2L);
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
mdnsClient.sendMulticastPacket(new DatagramPacket(buf, 0, 5)); mdnsClient.sendPacketRequestingMulticastResponse(getTestDatagramPacket(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
} }
synchronized (mdnsClient.multicastPacketQueue) { synchronized (mdnsClient.multicastPacketQueue) {
@@ -452,9 +460,11 @@ public class MdnsSocketClientTests {
enableUnicastResponse.set(true); enableUnicastResponse.set(true);
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
DatagramPacket packet = new DatagramPacket(buf, 0, 5); DatagramPacket packet = getTestDatagramPacket();
mdnsClient.sendUnicastPacket(packet); mdnsClient.sendPacketRequestingUnicastResponse(packet,
mdnsClient.sendMulticastPacket(packet); false /* onlyUseIpv6OnIpv6OnlyNetworks */);
mdnsClient.sendPacketRequestingMulticastResponse(packet,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
// Wait for the timer to be triggered. // Wait for the timer to be triggered.
Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2); Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2);
@@ -484,8 +494,10 @@ public class MdnsSocketClientTests {
assertFalse(mdnsClient.receivedUnicastResponse); assertFalse(mdnsClient.receivedUnicastResponse);
assertFalse(mdnsClient.cannotReceiveMulticastResponse.get()); assertFalse(mdnsClient.cannotReceiveMulticastResponse.get());
mdnsClient.sendUnicastPacket(packet); mdnsClient.sendPacketRequestingUnicastResponse(packet,
mdnsClient.sendMulticastPacket(packet); false /* onlyUseIpv6OnIpv6OnlyNetworks */);
mdnsClient.sendPacketRequestingMulticastResponse(packet,
false /* onlyUseIpv6OnIpv6OnlyNetworks */);
Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2); Thread.sleep(MdnsConfigs.checkMulticastResponseIntervalMs() * 2);
// Verify cannotReceiveMulticastResponse is not set the true because we didn't receive the // 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)) verify(mockCallback, timeout(TIMEOUT).atLeast(1))
.onResponseReceived(any(), argThat(key -> key.getInterfaceIndex() == -1)); .onResponseReceived(any(), argThat(key -> key.getInterfaceIndex() == -1));
} }
private DatagramPacket getTestDatagramPacket() {
return new DatagramPacket(buf, 0, 5,
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), 5353 /* port */));
}
} }