Merge "Create an MdnsServiceTypeClient using a SocketKey"

This commit is contained in:
Paul Hu
2023-06-15 01:01:46 +00:00
committed by Gerrit Code Review
9 changed files with 201 additions and 198 deletions

View File

@@ -22,7 +22,6 @@ import android.Manifest.permission;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.RequiresPermission; import android.annotation.RequiresPermission;
import android.net.Network;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -36,7 +35,6 @@ import com.android.server.connectivity.mdns.util.MdnsUtils;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* This class keeps tracking the set of registered {@link MdnsServiceBrowserListener} instances, and * This class keeps tracking the set of registered {@link MdnsServiceBrowserListener} instances, and
@@ -50,54 +48,58 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
private final MdnsSocketClientBase socketClient; private final MdnsSocketClientBase socketClient;
@NonNull private final SharedLog sharedLog; @NonNull private final SharedLog sharedLog;
@NonNull private final PerNetworkServiceTypeClients perNetworkServiceTypeClients; @NonNull private final PerSocketServiceTypeClients perSocketServiceTypeClients;
@NonNull private final Handler handler; @NonNull private final Handler handler;
@Nullable private final HandlerThread handlerThread; @Nullable private final HandlerThread handlerThread;
private static class PerNetworkServiceTypeClients { private static class PerSocketServiceTypeClients {
private final ArrayMap<Pair<String, Network>, MdnsServiceTypeClient> clients = private final ArrayMap<Pair<String, SocketKey>, MdnsServiceTypeClient> clients =
new ArrayMap<>(); new ArrayMap<>();
public void put(@NonNull String serviceType, @Nullable Network network, public void put(@NonNull String serviceType, @NonNull SocketKey socketKey,
@NonNull MdnsServiceTypeClient client) { @NonNull MdnsServiceTypeClient client) {
final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType); final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType, final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsLowerServiceType,
network); socketKey);
clients.put(perNetworkServiceType, client); clients.put(perSocketServiceType, client);
} }
@Nullable @Nullable
public MdnsServiceTypeClient get(@NonNull String serviceType, @Nullable Network network) { public MdnsServiceTypeClient get(
@NonNull String serviceType, @NonNull SocketKey socketKey) {
final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType); final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
final Pair<String, Network> perNetworkServiceType = new Pair<>(dnsLowerServiceType, final Pair<String, SocketKey> perSocketServiceType = new Pair<>(dnsLowerServiceType,
network); socketKey);
return clients.getOrDefault(perNetworkServiceType, null); return clients.getOrDefault(perSocketServiceType, null);
} }
public List<MdnsServiceTypeClient> getByServiceType(@NonNull String serviceType) { public List<MdnsServiceTypeClient> getByServiceType(@NonNull String serviceType) {
final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType); final String dnsLowerServiceType = MdnsUtils.toDnsLowerCase(serviceType);
final List<MdnsServiceTypeClient> list = new ArrayList<>(); final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) { for (int i = 0; i < clients.size(); i++) {
final Pair<String, Network> perNetworkServiceType = clients.keyAt(i); final Pair<String, SocketKey> perSocketServiceType = clients.keyAt(i);
if (dnsLowerServiceType.equals(perNetworkServiceType.first)) { if (dnsLowerServiceType.equals(perSocketServiceType.first)) {
list.add(clients.valueAt(i)); list.add(clients.valueAt(i));
} }
} }
return list; return list;
} }
public List<MdnsServiceTypeClient> getByNetwork(@Nullable Network network) { public List<MdnsServiceTypeClient> getBySocketKey(@NonNull SocketKey socketKey) {
final List<MdnsServiceTypeClient> list = new ArrayList<>(); final List<MdnsServiceTypeClient> list = new ArrayList<>();
for (int i = 0; i < clients.size(); i++) { for (int i = 0; i < clients.size(); i++) {
final Pair<String, Network> perNetworkServiceType = clients.keyAt(i); final Pair<String, SocketKey> perSocketServiceType = clients.keyAt(i);
final Network serviceTypeNetwork = perNetworkServiceType.second; if (socketKey.equals(perSocketServiceType.second)) {
if (Objects.equals(network, serviceTypeNetwork)) {
list.add(clients.valueAt(i)); list.add(clients.valueAt(i));
} }
} }
return list; return list;
} }
public List<MdnsServiceTypeClient> getAllMdnsServiceTypeClient() {
return new ArrayList<>(clients.values());
}
public void remove(@NonNull MdnsServiceTypeClient client) { public void remove(@NonNull MdnsServiceTypeClient client) {
final int index = clients.indexOfValue(client); final int index = clients.indexOfValue(client);
clients.removeAt(index); clients.removeAt(index);
@@ -113,7 +115,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
this.executorProvider = executorProvider; this.executorProvider = executorProvider;
this.socketClient = socketClient; this.socketClient = socketClient;
this.sharedLog = sharedLog; this.sharedLog = sharedLog;
this.perNetworkServiceTypeClients = new PerNetworkServiceTypeClients(); this.perSocketServiceTypeClients = new PerSocketServiceTypeClients();
if (socketClient.getLooper() != null) { if (socketClient.getLooper() != null) {
this.handlerThread = null; this.handlerThread = null;
this.handler = new Handler(socketClient.getLooper()); this.handler = new Handler(socketClient.getLooper());
@@ -164,7 +166,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
@NonNull String serviceType, @NonNull String serviceType,
@NonNull MdnsServiceBrowserListener listener, @NonNull MdnsServiceBrowserListener listener,
@NonNull MdnsSearchOptions searchOptions) { @NonNull MdnsSearchOptions searchOptions) {
if (perNetworkServiceTypeClients.isEmpty()) { if (perSocketServiceTypeClients.isEmpty()) {
// First listener. Starts the socket client. // First listener. Starts the socket client.
try { try {
socketClient.startDiscovery(); socketClient.startDiscovery();
@@ -177,29 +179,29 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork(), socketClient.notifyNetworkRequested(listener, searchOptions.getNetwork(),
new MdnsSocketClientBase.SocketCreationCallback() { new MdnsSocketClientBase.SocketCreationCallback() {
@Override @Override
public void onSocketCreated(@Nullable Network network) { public void onSocketCreated(@NonNull SocketKey socketKey) {
ensureRunningOnHandlerThread(handler); ensureRunningOnHandlerThread(handler);
// All listeners of the same service types shares the same // All listeners of the same service types shares the same
// MdnsServiceTypeClient. // MdnsServiceTypeClient.
MdnsServiceTypeClient serviceTypeClient = MdnsServiceTypeClient serviceTypeClient =
perNetworkServiceTypeClients.get(serviceType, network); perSocketServiceTypeClients.get(serviceType, socketKey);
if (serviceTypeClient == null) { if (serviceTypeClient == null) {
serviceTypeClient = createServiceTypeClient(serviceType, network); serviceTypeClient = createServiceTypeClient(serviceType, socketKey);
perNetworkServiceTypeClients.put(serviceType, network, perSocketServiceTypeClients.put(serviceType, socketKey,
serviceTypeClient); serviceTypeClient);
} }
serviceTypeClient.startSendAndReceive(listener, searchOptions); serviceTypeClient.startSendAndReceive(listener, searchOptions);
} }
@Override @Override
public void onAllSocketsDestroyed(@Nullable Network network) { public void onAllSocketsDestroyed(@NonNull SocketKey socketKey) {
ensureRunningOnHandlerThread(handler); ensureRunningOnHandlerThread(handler);
final MdnsServiceTypeClient serviceTypeClient = final MdnsServiceTypeClient serviceTypeClient =
perNetworkServiceTypeClients.get(serviceType, network); perSocketServiceTypeClients.get(serviceType, socketKey);
if (serviceTypeClient == null) return; if (serviceTypeClient == null) return;
// Notify all listeners that all services are removed from this socket. // Notify all listeners that all services are removed from this socket.
serviceTypeClient.notifySocketDestroyed(); serviceTypeClient.notifySocketDestroyed();
perNetworkServiceTypeClients.remove(serviceTypeClient); perSocketServiceTypeClients.remove(serviceTypeClient);
} }
}); });
} }
@@ -224,7 +226,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
socketClient.notifyNetworkUnrequested(listener); socketClient.notifyNetworkUnrequested(listener);
final List<MdnsServiceTypeClient> serviceTypeClients = final List<MdnsServiceTypeClient> serviceTypeClients =
perNetworkServiceTypeClients.getByServiceType(serviceType); perSocketServiceTypeClients.getByServiceType(serviceType);
if (serviceTypeClients.isEmpty()) { if (serviceTypeClients.isEmpty()) {
return; return;
} }
@@ -233,10 +235,10 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
if (serviceTypeClient.stopSendAndReceive(listener)) { if (serviceTypeClient.stopSendAndReceive(listener)) {
// No listener is registered for the service type anymore, remove it from the list // No listener is registered for the service type anymore, remove it from the list
// of the service type clients. // of the service type clients.
perNetworkServiceTypeClients.remove(serviceTypeClient); perSocketServiceTypeClients.remove(serviceTypeClient);
} }
} }
if (perNetworkServiceTypeClients.isEmpty()) { if (perSocketServiceTypeClients.isEmpty()) {
// No discovery request. Stops the socket client. // No discovery request. Stops the socket client.
sharedLog.i("All service type listeners unregistered; stopping discovery"); sharedLog.i("All service type listeners unregistered; stopping discovery");
socketClient.stopDiscovery(); socketClient.stopDiscovery();
@@ -244,50 +246,48 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback {
} }
@Override @Override
public void onResponseReceived(@NonNull MdnsPacket packet, public void onResponseReceived(@NonNull MdnsPacket packet, @NonNull SocketKey socketKey) {
int interfaceIndex, @Nullable Network network) {
checkAndRunOnHandlerThread(() -> checkAndRunOnHandlerThread(() ->
handleOnResponseReceived(packet, interfaceIndex, network)); handleOnResponseReceived(packet, socketKey));
} }
private void handleOnResponseReceived(@NonNull MdnsPacket packet, int interfaceIndex, private void handleOnResponseReceived(@NonNull MdnsPacket packet,
@Nullable Network network) { @NonNull SocketKey socketKey) {
for (MdnsServiceTypeClient serviceTypeClient for (MdnsServiceTypeClient serviceTypeClient : getMdnsServiceTypeClient(socketKey)) {
: getMdnsServiceTypeClient(network)) { serviceTypeClient.processResponse(
serviceTypeClient.processResponse(packet, interfaceIndex, network); packet, socketKey.getInterfaceIndex(), socketKey.getNetwork());
} }
} }
private List<MdnsServiceTypeClient> getMdnsServiceTypeClient(@Nullable Network network) { private List<MdnsServiceTypeClient> getMdnsServiceTypeClient(@NonNull SocketKey socketKey) {
if (socketClient.supportsRequestingSpecificNetworks()) { if (socketClient.supportsRequestingSpecificNetworks()) {
return perNetworkServiceTypeClients.getByNetwork(network); return perSocketServiceTypeClients.getBySocketKey(socketKey);
} else { } else {
return perNetworkServiceTypeClients.getByNetwork(null); return perSocketServiceTypeClients.getAllMdnsServiceTypeClient();
} }
} }
@Override @Override
public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode, public void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
@Nullable Network network) { @NonNull SocketKey socketKey) {
checkAndRunOnHandlerThread(() -> checkAndRunOnHandlerThread(() ->
handleOnFailedToParseMdnsResponse(receivedPacketNumber, errorCode, network)); handleOnFailedToParseMdnsResponse(receivedPacketNumber, errorCode, socketKey));
} }
private void handleOnFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode, private void handleOnFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
@Nullable Network network) { @NonNull SocketKey socketKey) {
for (MdnsServiceTypeClient serviceTypeClient for (MdnsServiceTypeClient serviceTypeClient : getMdnsServiceTypeClient(socketKey)) {
: getMdnsServiceTypeClient(network)) {
serviceTypeClient.onFailedToParseMdnsResponse(receivedPacketNumber, errorCode); serviceTypeClient.onFailedToParseMdnsResponse(receivedPacketNumber, errorCode);
} }
} }
@VisibleForTesting @VisibleForTesting
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType, MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@Nullable Network network) { @NonNull SocketKey socketKey) {
sharedLog.log("createServiceTypeClient for type:" + serviceType + ", net:" + network); sharedLog.log("createServiceTypeClient for type:" + serviceType + " " + socketKey);
return new MdnsServiceTypeClient( return new MdnsServiceTypeClient(
serviceType, socketClient, serviceType, socketClient,
executorProvider.newServiceTypeClientSchedulerExecutor(), network, executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey,
sharedLog.forSubComponent(serviceType + "-" + network)); sharedLog.forSubComponent(serviceType + "-" + socketKey));
} }
} }

View File

@@ -84,7 +84,7 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
} }
socket.addPacketHandler(handler); socket.addPacketHandler(handler);
mActiveNetworkSockets.put(socket, socketKey); mActiveNetworkSockets.put(socket, socketKey);
mSocketCreationCallback.onSocketCreated(socketKey.getNetwork()); mSocketCreationCallback.onSocketCreated(socketKey);
} }
@Override @Override
@@ -97,7 +97,7 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) { private void notifySocketDestroyed(@NonNull MdnsInterfaceSocket socket) {
final SocketKey socketKey = mActiveNetworkSockets.remove(socket); final SocketKey socketKey = mActiveNetworkSockets.remove(socket);
if (!isAnySocketActive(socketKey)) { if (!isAnySocketActive(socketKey)) {
mSocketCreationCallback.onAllSocketsDestroyed(socketKey.getNetwork()); mSocketCreationCallback.onAllSocketsDestroyed(socketKey);
} }
} }
@@ -247,16 +247,14 @@ public class MdnsMultinetworkSocketClient implements MdnsSocketClientBase {
if (e.code != MdnsResponseErrorCode.ERROR_NOT_RESPONSE_MESSAGE) { if (e.code != MdnsResponseErrorCode.ERROR_NOT_RESPONSE_MESSAGE) {
Log.e(TAG, e.getMessage(), e); Log.e(TAG, e.getMessage(), e);
if (mCallback != null) { if (mCallback != null) {
mCallback.onFailedToParseMdnsResponse( mCallback.onFailedToParseMdnsResponse(packetNumber, e.code, socketKey);
packetNumber, e.code, socketKey.getNetwork());
} }
} }
return; return;
} }
if (mCallback != null) { if (mCallback != null) {
mCallback.onResponseReceived( mCallback.onResponseReceived(response, socketKey);
response, socketKey.getInterfaceIndex(), socketKey.getNetwork());
} }
} }

View File

@@ -58,7 +58,7 @@ public class MdnsServiceTypeClient {
private final MdnsSocketClientBase socketClient; private final MdnsSocketClientBase socketClient;
private final MdnsResponseDecoder responseDecoder; private final MdnsResponseDecoder responseDecoder;
private final ScheduledExecutorService executor; private final ScheduledExecutorService executor;
@Nullable private final Network network; @NonNull private final SocketKey socketKey;
@NonNull private final SharedLog sharedLog; @NonNull private final SharedLog sharedLog;
private final Object lock = new Object(); private final Object lock = new Object();
private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners = private final ArrayMap<MdnsServiceBrowserListener, MdnsSearchOptions> listeners =
@@ -90,9 +90,9 @@ public class MdnsServiceTypeClient {
@NonNull String serviceType, @NonNull String serviceType,
@NonNull MdnsSocketClientBase socketClient, @NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor, @NonNull ScheduledExecutorService executor,
@Nullable Network network, @NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog) { @NonNull SharedLog sharedLog) {
this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), network, this(serviceType, socketClient, executor, new MdnsResponseDecoder.Clock(), socketKey,
sharedLog); sharedLog);
} }
@@ -102,7 +102,7 @@ public class MdnsServiceTypeClient {
@NonNull MdnsSocketClientBase socketClient, @NonNull MdnsSocketClientBase socketClient,
@NonNull ScheduledExecutorService executor, @NonNull ScheduledExecutorService executor,
@NonNull MdnsResponseDecoder.Clock clock, @NonNull MdnsResponseDecoder.Clock clock,
@Nullable Network network, @NonNull SocketKey socketKey,
@NonNull SharedLog sharedLog) { @NonNull SharedLog sharedLog) {
this.serviceType = serviceType; this.serviceType = serviceType;
this.socketClient = socketClient; this.socketClient = socketClient;
@@ -110,7 +110,7 @@ public class MdnsServiceTypeClient {
this.serviceTypeLabels = TextUtils.split(serviceType, "\\."); this.serviceTypeLabels = TextUtils.split(serviceType, "\\.");
this.responseDecoder = new MdnsResponseDecoder(clock, serviceTypeLabels); this.responseDecoder = new MdnsResponseDecoder(clock, serviceTypeLabels);
this.clock = clock; this.clock = clock;
this.network = network; this.socketKey = socketKey;
this.sharedLog = sharedLog; this.sharedLog = sharedLog;
} }
@@ -199,7 +199,7 @@ public class MdnsServiceTypeClient {
searchOptions.getSubtypes(), searchOptions.getSubtypes(),
searchOptions.isPassiveMode(), searchOptions.isPassiveMode(),
currentSessionId, currentSessionId,
network); socketKey);
if (hadReply) { if (hadReply) {
requestTaskFuture = scheduleNextRunLocked(taskConfig); requestTaskFuture = scheduleNextRunLocked(taskConfig);
} else { } else {
@@ -437,10 +437,10 @@ public class MdnsServiceTypeClient {
private int burstCounter; private int burstCounter;
private int timeToRunNextTaskInMs; private int timeToRunNextTaskInMs;
private boolean isFirstBurst; private boolean isFirstBurst;
@Nullable private final Network network; @NonNull private final SocketKey socketKey;
QueryTaskConfig(@NonNull Collection<String> subtypes, boolean usePassiveMode, QueryTaskConfig(@NonNull Collection<String> subtypes, boolean usePassiveMode,
long sessionId, @Nullable Network network) { long sessionId, @NonNull SocketKey socketKey) {
this.usePassiveMode = usePassiveMode; this.usePassiveMode = usePassiveMode;
this.subtypes = new ArrayList<>(subtypes); this.subtypes = new ArrayList<>(subtypes);
this.queriesPerBurst = QUERIES_PER_BURST; this.queriesPerBurst = QUERIES_PER_BURST;
@@ -462,7 +462,7 @@ public class MdnsServiceTypeClient {
// doubles until it maxes out at TIME_BETWEEN_BURSTS_MS. // doubles until it maxes out at TIME_BETWEEN_BURSTS_MS.
this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS; this.timeBetweenBurstsInMs = INITIAL_TIME_BETWEEN_BURSTS_MS;
} }
this.network = network; this.socketKey = socketKey;
} }
QueryTaskConfig getConfigForNextRun() { QueryTaskConfig getConfigForNextRun() {
@@ -545,7 +545,7 @@ public class MdnsServiceTypeClient {
// Only the names are used to know which queries to send, other parameters like // Only the names are used to know which queries to send, other parameters like
// interfaceIndex do not matter. // interfaceIndex do not matter.
servicesToResolve = makeResponsesForResolve( servicesToResolve = makeResponsesForResolve(
0 /* interfaceIndex */, config.network); 0 /* interfaceIndex */, config.socketKey.getNetwork());
sendDiscoveryQueries = servicesToResolve.size() < listeners.size(); sendDiscoveryQueries = servicesToResolve.size() < listeners.size();
} }
Pair<Integer, List<String>> result; Pair<Integer, List<String>> result;
@@ -558,7 +558,7 @@ public class MdnsServiceTypeClient {
config.subtypes, config.subtypes,
config.expectUnicastResponse, config.expectUnicastResponse,
config.transactionId, config.transactionId,
config.network, config.socketKey.getNetwork(),
sendDiscoveryQueries, sendDiscoveryQueries,
servicesToResolve, servicesToResolve,
clock) clock)

View File

@@ -235,7 +235,7 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
throw new IllegalArgumentException("This socket client does not support requesting " throw new IllegalArgumentException("This socket client does not support requesting "
+ "specific networks"); + "specific networks");
} }
socketCreationCallback.onSocketCreated(null); socketCreationCallback.onSocketCreated(new SocketKey(multicastSocket.getInterfaceIndex()));
} }
@Override @Override
@@ -456,7 +456,8 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
LOGGER.w(String.format("Error while decoding %s packet (%d): %d", LOGGER.w(String.format("Error while decoding %s packet (%d): %d",
responseType, packetNumber, e.code)); responseType, packetNumber, e.code));
if (callback != null) { if (callback != null) {
callback.onFailedToParseMdnsResponse(packetNumber, e.code, network); callback.onFailedToParseMdnsResponse(packetNumber, e.code,
new SocketKey(network, interfaceIndex));
} }
return e.code; return e.code;
} }
@@ -466,7 +467,8 @@ public class MdnsSocketClient implements MdnsSocketClientBase {
} }
if (callback != null) { if (callback != null) {
callback.onResponseReceived(response, interfaceIndex, network); callback.onResponseReceived(
response, new SocketKey(network, interfaceIndex));
} }
return MdnsResponseErrorCode.SUCCESS; return MdnsResponseErrorCode.SUCCESS;

View File

@@ -73,20 +73,19 @@ public interface MdnsSocketClientBase {
/*** Callback for mdns response */ /*** Callback for mdns response */
interface Callback { interface Callback {
/*** Receive a mdns response */ /*** Receive a mdns response */
void onResponseReceived(@NonNull MdnsPacket packet, int interfaceIndex, void onResponseReceived(@NonNull MdnsPacket packet, @NonNull SocketKey socketKey);
@Nullable Network network);
/*** Parse a mdns response failed */ /*** Parse a mdns response failed */
void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode, void onFailedToParseMdnsResponse(int receivedPacketNumber, int errorCode,
@Nullable Network network); @NonNull SocketKey socketKey);
} }
/*** Callback for requested socket creation */ /*** Callback for requested socket creation */
interface SocketCreationCallback { interface SocketCreationCallback {
/*** Notify requested socket is created */ /*** Notify requested socket is created */
void onSocketCreated(@Nullable Network network); void onSocketCreated(@NonNull SocketKey socketKey);
/*** Notify requested socket is destroyed */ /*** Notify requested socket is destroyed */
void onAllSocketsDestroyed(@Nullable Network network); void onAllSocketsDestroyed(@NonNull SocketKey socketKey);
} }
} }

View File

@@ -28,7 +28,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network; import android.net.Network;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
@@ -65,17 +64,22 @@ public class MdnsDiscoveryManagerTests {
private static final String SERVICE_TYPE_2 = "_test._tcp.local"; private static final String SERVICE_TYPE_2 = "_test._tcp.local";
private static final Network NETWORK_1 = Mockito.mock(Network.class); private static final Network NETWORK_1 = Mockito.mock(Network.class);
private static final Network NETWORK_2 = Mockito.mock(Network.class); private static final Network NETWORK_2 = Mockito.mock(Network.class);
private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_1_NULL_NETWORK = private static final SocketKey SOCKET_KEY_NULL_NETWORK =
Pair.create(SERVICE_TYPE_1, null); new SocketKey(null /* network */, 999 /* interfaceIndex */);
private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_1_NETWORK_1 = private static final SocketKey SOCKET_KEY_NETWORK_1 =
Pair.create(SERVICE_TYPE_1, NETWORK_1); new SocketKey(NETWORK_1, 998 /* interfaceIndex */);
private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NULL_NETWORK = private static final SocketKey SOCKET_KEY_NETWORK_2 =
Pair.create(SERVICE_TYPE_2, null); new SocketKey(NETWORK_2, 997 /* interfaceIndex */);
private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NETWORK_1 = private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_1_NULL_NETWORK =
Pair.create(SERVICE_TYPE_2, NETWORK_1); Pair.create(SERVICE_TYPE_1, SOCKET_KEY_NULL_NETWORK);
private static final Pair<String, Network> PER_NETWORK_SERVICE_TYPE_2_NETWORK_2 = private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_2_NULL_NETWORK =
Pair.create(SERVICE_TYPE_2, NETWORK_2); Pair.create(SERVICE_TYPE_2, SOCKET_KEY_NULL_NETWORK);
private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_1_NETWORK_1 =
Pair.create(SERVICE_TYPE_1, SOCKET_KEY_NETWORK_1);
private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_2_NETWORK_1 =
Pair.create(SERVICE_TYPE_2, SOCKET_KEY_NETWORK_1);
private static final Pair<String, SocketKey> PER_SOCKET_SERVICE_TYPE_2_NETWORK_2 =
Pair.create(SERVICE_TYPE_2, SOCKET_KEY_NETWORK_2);
@Mock private ExecutorProvider executorProvider; @Mock private ExecutorProvider executorProvider;
@Mock private MdnsSocketClientBase socketClient; @Mock private MdnsSocketClientBase socketClient;
@Mock private MdnsServiceTypeClient mockServiceTypeClientType1NullNetwork; @Mock private MdnsServiceTypeClient mockServiceTypeClientType1NullNetwork;
@@ -104,22 +108,22 @@ public class MdnsDiscoveryManagerTests {
sharedLog) { sharedLog) {
@Override @Override
MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType, MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType,
@Nullable Network network) { @NonNull SocketKey socketKey) {
final Pair<String, Network> perNetworkServiceType = final Pair<String, SocketKey> perSocketServiceType =
Pair.create(serviceType, network); Pair.create(serviceType, socketKey);
if (perNetworkServiceType.equals(PER_NETWORK_SERVICE_TYPE_1_NULL_NETWORK)) { if (perSocketServiceType.equals(PER_SOCKET_SERVICE_TYPE_1_NULL_NETWORK)) {
return mockServiceTypeClientType1NullNetwork; return mockServiceTypeClientType1NullNetwork;
} else if (perNetworkServiceType.equals( } else if (perSocketServiceType.equals(
PER_NETWORK_SERVICE_TYPE_1_NETWORK_1)) { PER_SOCKET_SERVICE_TYPE_1_NETWORK_1)) {
return mockServiceTypeClientType1Network1; return mockServiceTypeClientType1Network1;
} else if (perNetworkServiceType.equals( } else if (perSocketServiceType.equals(
PER_NETWORK_SERVICE_TYPE_2_NULL_NETWORK)) { PER_SOCKET_SERVICE_TYPE_2_NULL_NETWORK)) {
return mockServiceTypeClientType2NullNetwork; return mockServiceTypeClientType2NullNetwork;
} else if (perNetworkServiceType.equals( } else if (perSocketServiceType.equals(
PER_NETWORK_SERVICE_TYPE_2_NETWORK_1)) { PER_SOCKET_SERVICE_TYPE_2_NETWORK_1)) {
return mockServiceTypeClientType2Network1; return mockServiceTypeClientType2Network1;
} else if (perNetworkServiceType.equals( } else if (perSocketServiceType.equals(
PER_NETWORK_SERVICE_TYPE_2_NETWORK_2)) { PER_SOCKET_SERVICE_TYPE_2_NETWORK_2)) {
return mockServiceTypeClientType2Network2; return mockServiceTypeClientType2Network2;
} }
return null; return null;
@@ -156,7 +160,7 @@ public class MdnsDiscoveryManagerTests {
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build(); MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback( final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options); SERVICE_TYPE_1, mockListenerOne, options);
runOnHandler(() -> callback.onSocketCreated(null /* network */)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options); verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options);
when(mockServiceTypeClientType1NullNetwork.stopSendAndReceive(mockListenerOne)) when(mockServiceTypeClientType1NullNetwork.stopSendAndReceive(mockListenerOne))
@@ -172,16 +176,16 @@ public class MdnsDiscoveryManagerTests {
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build(); MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback( final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options); SERVICE_TYPE_1, mockListenerOne, options);
runOnHandler(() -> callback.onSocketCreated(null /* network */)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options); verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(mockListenerOne, options);
runOnHandler(() -> callback.onSocketCreated(NETWORK_1)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options); verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options);
final SocketCreationCallback callback2 = expectSocketCreationCallback( final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, options); SERVICE_TYPE_2, mockListenerTwo, options);
runOnHandler(() -> callback2.onSocketCreated(null /* network */)); runOnHandler(() -> callback2.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType2NullNetwork).startSendAndReceive(mockListenerTwo, options); verify(mockServiceTypeClientType2NullNetwork).startSendAndReceive(mockListenerTwo, options);
runOnHandler(() -> callback2.onSocketCreated(NETWORK_2)); runOnHandler(() -> callback2.onSocketCreated(SOCKET_KEY_NETWORK_2));
verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options); verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options);
} }
@@ -191,49 +195,48 @@ public class MdnsDiscoveryManagerTests {
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build(); MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback( final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, options1); SERVICE_TYPE_1, mockListenerOne, options1);
runOnHandler(() -> callback.onSocketCreated(null /* network */)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive( verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(
mockListenerOne, options1); mockListenerOne, options1);
runOnHandler(() -> callback.onSocketCreated(NETWORK_1)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options1); verify(mockServiceTypeClientType1Network1).startSendAndReceive(mockListenerOne, options1);
final MdnsSearchOptions options2 = final MdnsSearchOptions options2 =
MdnsSearchOptions.newBuilder().setNetwork(NETWORK_2).build(); MdnsSearchOptions.newBuilder().setNetwork(NETWORK_2).build();
final SocketCreationCallback callback2 = expectSocketCreationCallback( final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, options2); SERVICE_TYPE_2, mockListenerTwo, options2);
runOnHandler(() -> callback2.onSocketCreated(NETWORK_2)); runOnHandler(() -> callback2.onSocketCreated(SOCKET_KEY_NETWORK_2));
verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options2); verify(mockServiceTypeClientType2Network2).startSendAndReceive(mockListenerTwo, options2);
final MdnsPacket responseForServiceTypeOne = createMdnsPacket(SERVICE_TYPE_1); final MdnsPacket responseForServiceTypeOne = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1;
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(
responseForServiceTypeOne, ifIndex, null /* network */)); responseForServiceTypeOne, SOCKET_KEY_NULL_NETWORK));
// Packets for network null are only processed by the ServiceTypeClient for network null // Packets for network null are only processed by the ServiceTypeClient for network null
verify(mockServiceTypeClientType1NullNetwork).processResponse(responseForServiceTypeOne, verify(mockServiceTypeClientType1NullNetwork).processResponse(responseForServiceTypeOne,
ifIndex, null /* network */); SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(), any()); verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(), any());
verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(), any()); verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(), any());
final MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2); final MdnsPacket responseForServiceTypeTwo = createMdnsPacket(SERVICE_TYPE_2);
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(
responseForServiceTypeTwo, ifIndex, NETWORK_1)); responseForServiceTypeTwo, SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(), verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(),
eq(NETWORK_1)); eq(SOCKET_KEY_NETWORK_1.getNetwork()));
verify(mockServiceTypeClientType1Network1).processResponse(responseForServiceTypeTwo, verify(mockServiceTypeClientType1Network1).processResponse(responseForServiceTypeTwo,
ifIndex, NETWORK_1); SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(), verify(mockServiceTypeClientType2Network2, never()).processResponse(any(), anyInt(),
eq(NETWORK_1)); eq(SOCKET_KEY_NETWORK_1.getNetwork()));
final MdnsPacket responseForSubtype = final MdnsPacket responseForSubtype =
createMdnsPacket("subtype._sub._googlecast._tcp.local"); createMdnsPacket("subtype._sub._googlecast._tcp.local");
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(
responseForSubtype, ifIndex, NETWORK_2)); responseForSubtype, SOCKET_KEY_NETWORK_2));
verify(mockServiceTypeClientType1NullNetwork, never()).processResponse( verify(mockServiceTypeClientType1NullNetwork, never()).processResponse(any(), anyInt(),
any(), anyInt(), eq(NETWORK_2)); eq(SOCKET_KEY_NETWORK_2.getNetwork()));
verify(mockServiceTypeClientType1Network1, never()).processResponse( verify(mockServiceTypeClientType1Network1, never()).processResponse(any(), anyInt(),
any(), anyInt(), eq(NETWORK_2)); eq(SOCKET_KEY_NETWORK_2.getNetwork()));
verify(mockServiceTypeClientType2Network2).processResponse( verify(mockServiceTypeClientType2Network2).processResponse(responseForSubtype,
responseForSubtype, ifIndex, NETWORK_2); SOCKET_KEY_NETWORK_2.getInterfaceIndex(), SOCKET_KEY_NETWORK_2.getNetwork());
} }
@Test @Test
@@ -243,55 +246,53 @@ public class MdnsDiscoveryManagerTests {
MdnsSearchOptions.newBuilder().setNetwork(NETWORK_1).build(); MdnsSearchOptions.newBuilder().setNetwork(NETWORK_1).build();
final SocketCreationCallback callback = expectSocketCreationCallback( final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, network1Options); SERVICE_TYPE_1, mockListenerOne, network1Options);
runOnHandler(() -> callback.onSocketCreated(NETWORK_1)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1Network1).startSendAndReceive( verify(mockServiceTypeClientType1Network1).startSendAndReceive(
mockListenerOne, network1Options); mockListenerOne, network1Options);
// Create a ServiceTypeClient for SERVICE_TYPE_2 and NETWORK_1 // Create a ServiceTypeClient for SERVICE_TYPE_2 and NETWORK_1
final SocketCreationCallback callback2 = expectSocketCreationCallback( final SocketCreationCallback callback2 = expectSocketCreationCallback(
SERVICE_TYPE_2, mockListenerTwo, network1Options); SERVICE_TYPE_2, mockListenerTwo, network1Options);
runOnHandler(() -> callback2.onSocketCreated(NETWORK_1)); runOnHandler(() -> callback2.onSocketCreated(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType2Network1).startSendAndReceive( verify(mockServiceTypeClientType2Network1).startSendAndReceive(
mockListenerTwo, network1Options); mockListenerTwo, network1Options);
// Receive a response, it should be processed on both clients. // Receive a response, it should be processed on both clients.
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1); final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1; runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
runOnHandler(() -> discoveryManager.onResponseReceived( verify(mockServiceTypeClientType1Network1).processResponse(response,
response, ifIndex, NETWORK_1)); SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
verify(mockServiceTypeClientType1Network1).processResponse(response, ifIndex, NETWORK_1); verify(mockServiceTypeClientType2Network1).processResponse(response,
verify(mockServiceTypeClientType2Network1).processResponse(response, ifIndex, NETWORK_1); SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
// The first callback receives a notification that the network has been destroyed, // The first callback receives a notification that the network has been destroyed,
// mockServiceTypeClientOne1 should send service removed notifications and remove from the // mockServiceTypeClientOne1 should send service removed notifications and remove from the
// list of clients. // list of clients.
runOnHandler(() -> callback.onAllSocketsDestroyed(NETWORK_1)); runOnHandler(() -> callback.onAllSocketsDestroyed(SOCKET_KEY_NETWORK_1));
verify(mockServiceTypeClientType1Network1).notifySocketDestroyed(); verify(mockServiceTypeClientType1Network1).notifySocketDestroyed();
// Receive a response again, it should be processed only on // Receive a response again, it should be processed only on
// mockServiceTypeClientType2Network1. Because the mockServiceTypeClientType1Network1 is // mockServiceTypeClientType2Network1. Because the mockServiceTypeClientType1Network1 is
// removed from the list of clients, it is no longer able to process responses. // removed from the list of clients, it is no longer able to process responses.
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
response, ifIndex, NETWORK_1));
// Still times(1) as a response was received once previously // Still times(1) as a response was received once previously
verify(mockServiceTypeClientType1Network1, times(1)) verify(mockServiceTypeClientType1Network1, times(1)).processResponse(response,
.processResponse(response, ifIndex, NETWORK_1); SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
verify(mockServiceTypeClientType2Network1, times(2)) verify(mockServiceTypeClientType2Network1, times(2)).processResponse(response,
.processResponse(response, ifIndex, NETWORK_1); SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
// The client for NETWORK_1 receives the callback that the NETWORK_2 has been destroyed, // The client for NETWORK_1 receives the callback that the NETWORK_2 has been destroyed,
// mockServiceTypeClientTwo2 shouldn't send any notifications. // mockServiceTypeClientTwo2 shouldn't send any notifications.
runOnHandler(() -> callback2.onAllSocketsDestroyed(NETWORK_2)); runOnHandler(() -> callback2.onAllSocketsDestroyed(SOCKET_KEY_NETWORK_2));
verify(mockServiceTypeClientType2Network1, never()).notifySocketDestroyed(); verify(mockServiceTypeClientType2Network1, never()).notifySocketDestroyed();
// Receive a response again, mockServiceTypeClientType2Network1 is still in the list of // Receive a response again, mockServiceTypeClientType2Network1 is still in the list of
// clients, it's still able to process responses. // clients, it's still able to process responses.
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NETWORK_1));
response, ifIndex, NETWORK_1)); verify(mockServiceTypeClientType1Network1, times(1)).processResponse(response,
verify(mockServiceTypeClientType1Network1, times(1)) SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
.processResponse(response, ifIndex, NETWORK_1); verify(mockServiceTypeClientType2Network1, times(3)).processResponse(response,
verify(mockServiceTypeClientType2Network1, times(3)) SOCKET_KEY_NETWORK_1.getInterfaceIndex(), SOCKET_KEY_NETWORK_1.getNetwork());
.processResponse(response, ifIndex, NETWORK_1);
} }
@Test @Test
@@ -301,27 +302,25 @@ public class MdnsDiscoveryManagerTests {
MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build(); MdnsSearchOptions.newBuilder().setNetwork(null /* network */).build();
final SocketCreationCallback callback = expectSocketCreationCallback( final SocketCreationCallback callback = expectSocketCreationCallback(
SERVICE_TYPE_1, mockListenerOne, network1Options); SERVICE_TYPE_1, mockListenerOne, network1Options);
runOnHandler(() -> callback.onSocketCreated(null /* network */)); runOnHandler(() -> callback.onSocketCreated(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive( verify(mockServiceTypeClientType1NullNetwork).startSendAndReceive(
mockListenerOne, network1Options); mockListenerOne, network1Options);
// Receive a response, it should be processed on the client. // Receive a response, it should be processed on the client.
final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1); final MdnsPacket response = createMdnsPacket(SERVICE_TYPE_1);
final int ifIndex = 1; final int ifIndex = 1;
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NULL_NETWORK));
response, ifIndex, null /* network */)); verify(mockServiceTypeClientType1NullNetwork).processResponse(response,
verify(mockServiceTypeClientType1NullNetwork).processResponse( SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
response, ifIndex, null /* network */);
runOnHandler(() -> callback.onAllSocketsDestroyed(null /* network */)); runOnHandler(() -> callback.onAllSocketsDestroyed(SOCKET_KEY_NULL_NETWORK));
verify(mockServiceTypeClientType1NullNetwork).notifySocketDestroyed(); verify(mockServiceTypeClientType1NullNetwork).notifySocketDestroyed();
// Receive a response again, it should not be processed. // Receive a response again, it should not be processed.
runOnHandler(() -> discoveryManager.onResponseReceived( runOnHandler(() -> discoveryManager.onResponseReceived(response, SOCKET_KEY_NULL_NETWORK));
response, ifIndex, null /* network */));
// Still times(1) as a response was received once previously // Still times(1) as a response was received once previously
verify(mockServiceTypeClientType1NullNetwork, times(1)) verify(mockServiceTypeClientType1NullNetwork, times(1)).processResponse(response,
.processResponse(response, ifIndex, null /* network */); SOCKET_KEY_NULL_NETWORK.getInterfaceIndex(), SOCKET_KEY_NULL_NETWORK.getNetwork());
// Unregister the listener, notifyNetworkUnrequested should be called but other stop methods // 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 // won't be call because the service type client was unregistered and destroyed. But those

View File

@@ -21,7 +21,6 @@ import static com.android.server.connectivity.mdns.MulticastPacketReader.PacketH
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -132,11 +131,11 @@ public class MdnsMultinetworkSocketClientTest {
doReturn(null).when(tetherSocketKey2).getNetwork(); doReturn(null).when(tetherSocketKey2).getNetwork();
// Notify socket created // Notify socket created
callback.onSocketCreated(mSocketKey, mSocket, List.of()); callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork); verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
callback.onSocketCreated(tetherSocketKey1, tetherIfaceSock1, List.of()); callback.onSocketCreated(tetherSocketKey1, tetherIfaceSock1, List.of());
verify(mSocketCreationCallback).onSocketCreated(null); verify(mSocketCreationCallback).onSocketCreated(tetherSocketKey1);
callback.onSocketCreated(tetherSocketKey2, tetherIfaceSock2, List.of()); callback.onSocketCreated(tetherSocketKey2, tetherIfaceSock2, List.of());
verify(mSocketCreationCallback, times(2)).onSocketCreated(null); 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.sendMulticastPacket(ipv4Packet, mNetwork);
@@ -172,7 +171,7 @@ public class MdnsMultinetworkSocketClientTest {
doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
// Notify socket created // Notify socket created
callback.onSocketCreated(mSocketKey, mSocket, List.of()); callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork); verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
final ArgumentCaptor<PacketHandler> handlerCaptor = final ArgumentCaptor<PacketHandler> handlerCaptor =
ArgumentCaptor.forClass(PacketHandler.class); ArgumentCaptor.forClass(PacketHandler.class);
@@ -183,7 +182,7 @@ public class MdnsMultinetworkSocketClientTest {
handler.handlePacket(data, data.length, null /* src */); handler.handlePacket(data, data.length, null /* src */);
final ArgumentCaptor<MdnsPacket> responseCaptor = final ArgumentCaptor<MdnsPacket> responseCaptor =
ArgumentCaptor.forClass(MdnsPacket.class); ArgumentCaptor.forClass(MdnsPacket.class);
verify(mCallback).onResponseReceived(responseCaptor.capture(), anyInt(), any()); verify(mCallback).onResponseReceived(responseCaptor.capture(), any());
final MdnsPacket response = responseCaptor.getValue(); final MdnsPacket response = responseCaptor.getValue();
assertEquals(0, response.questions.size()); assertEquals(0, response.questions.size());
assertEquals(0, response.additionalRecords.size()); assertEquals(0, response.additionalRecords.size());
@@ -222,12 +221,13 @@ public class MdnsMultinetworkSocketClientTest {
doReturn(createEmptyNetworkInterface()).when(socket3).getInterface(); doReturn(createEmptyNetworkInterface()).when(socket3).getInterface();
final SocketKey socketKey2 = mock(SocketKey.class); final SocketKey socketKey2 = mock(SocketKey.class);
doReturn(null).when(socketKey2).getNetwork(); final SocketKey socketKey3 = mock(SocketKey.class);
callback.onSocketCreated(mSocketKey, mSocket, List.of()); callback.onSocketCreated(mSocketKey, mSocket, List.of());
callback.onSocketCreated(socketKey2, socket2, List.of()); callback.onSocketCreated(socketKey2, socket2, List.of());
callback.onSocketCreated(socketKey2, socket3, List.of()); callback.onSocketCreated(socketKey3, socket3, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork); verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
verify(mSocketCreationCallback, times(2)).onSocketCreated(null); verify(mSocketCreationCallback).onSocketCreated(socketKey2);
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.sendMulticastPacket(ipv4Packet, mNetwork);
@@ -252,9 +252,10 @@ public class MdnsMultinetworkSocketClientTest {
// Notify socket created for all networks. // Notify socket created for all networks.
callback2.onSocketCreated(mSocketKey, mSocket, List.of()); callback2.onSocketCreated(mSocketKey, mSocket, List.of());
callback2.onSocketCreated(socketKey2, socket2, List.of()); callback2.onSocketCreated(socketKey2, socket2, List.of());
callback2.onSocketCreated(socketKey2, socket3, List.of()); callback2.onSocketCreated(socketKey3, socket3, List.of());
verify(socketCreationCb2).onSocketCreated(mNetwork); verify(socketCreationCb2).onSocketCreated(mSocketKey);
verify(socketCreationCb2, times(2)).onSocketCreated(null); verify(socketCreationCb2).onSocketCreated(socketKey2);
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.sendMulticastPacket(ipv4Packet, null);
@@ -296,16 +297,16 @@ public class MdnsMultinetworkSocketClientTest {
doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface(); doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
callback.onSocketCreated(mSocketKey, mSocket, List.of()); callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork); verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
callback.onSocketCreated(mSocketKey, otherSocket, List.of()); callback.onSocketCreated(mSocketKey, otherSocket, List.of());
verify(mSocketCreationCallback, times(2)).onSocketCreated(mNetwork); verify(mSocketCreationCallback, times(2)).onSocketCreated(mSocketKey);
verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(mNetwork); verify(mSocketCreationCallback, never()).onAllSocketsDestroyed(mSocketKey);
mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener)); mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mProvider).unrequestSocket(callback); verify(mProvider).unrequestSocket(callback);
verify(mSocketCreationCallback).onAllSocketsDestroyed(mNetwork); verify(mSocketCreationCallback).onAllSocketsDestroyed(mSocketKey);
} }
@Test @Test
@@ -316,14 +317,14 @@ public class MdnsMultinetworkSocketClientTest {
doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface(); doReturn(createEmptyNetworkInterface()).when(otherSocket).getInterface();
callback.onSocketCreated(mSocketKey, mSocket, List.of()); callback.onSocketCreated(mSocketKey, mSocket, List.of());
verify(mSocketCreationCallback).onSocketCreated(mNetwork); verify(mSocketCreationCallback).onSocketCreated(mSocketKey);
callback.onSocketCreated(mSocketKey, otherSocket, List.of()); callback.onSocketCreated(mSocketKey, otherSocket, List.of());
verify(mSocketCreationCallback, times(2)).onSocketCreated(mNetwork); verify(mSocketCreationCallback, times(2)).onSocketCreated(mSocketKey);
// Notify socket destroyed // Notify socket destroyed
callback.onInterfaceDestroyed(mSocketKey, mSocket); callback.onInterfaceDestroyed(mSocketKey, mSocket);
verifyNoMoreInteractions(mSocketCreationCallback); verifyNoMoreInteractions(mSocketCreationCallback);
callback.onInterfaceDestroyed(mSocketKey, otherSocket); callback.onInterfaceDestroyed(mSocketKey, otherSocket);
verify(mSocketCreationCallback).onAllSocketsDestroyed(mNetwork); verify(mSocketCreationCallback).onAllSocketsDestroyed(mSocketKey);
} }
} }

View File

@@ -118,6 +118,7 @@ public class MdnsServiceTypeClientTests {
private FakeExecutor currentThreadExecutor = new FakeExecutor(); private FakeExecutor currentThreadExecutor = new FakeExecutor();
private MdnsServiceTypeClient client; private MdnsServiceTypeClient client;
private SocketKey socketKey;
@Before @Before
@SuppressWarnings("DoNotMock") @SuppressWarnings("DoNotMock")
@@ -128,6 +129,7 @@ public class MdnsServiceTypeClientTests {
expectedIPv4Packets = new DatagramPacket[16]; expectedIPv4Packets = new DatagramPacket[16];
expectedIPv6Packets = new DatagramPacket[16]; expectedIPv6Packets = new DatagramPacket[16];
expectedSendFutures = new ScheduledFuture<?>[16]; expectedSendFutures = new ScheduledFuture<?>[16];
socketKey = new SocketKey(mockNetwork, INTERFACE_INDEX);
for (int i = 0; i < expectedSendFutures.length; ++i) { for (int i = 0; i < expectedSendFutures.length; ++i) {
expectedIPv4Packets[i] = new DatagramPacket(buf, 0 /* offset */, 5 /* length */, expectedIPv4Packets[i] = new DatagramPacket(buf, 0 /* offset */, 5 /* length */,
@@ -174,7 +176,7 @@ public class MdnsServiceTypeClientTests {
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, mockNetwork, mockSharedLog) { mockDecoderClock, socketKey, mockSharedLog) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -325,7 +327,7 @@ 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, mockNetwork); searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 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);
@@ -354,7 +356,7 @@ 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, mockNetwork); searchOptions.getSubtypes(), searchOptions.isPassiveMode(), 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);
@@ -508,9 +510,9 @@ public class MdnsServiceTypeClientTests {
// Process a second response with a different port and updated text attributes. // Process a second response with a different port and updated text attributes.
client.processResponse(createResponse( client.processResponse(createResponse(
"service-instance-1", ipV4Address, 5354, "service-instance-1", ipV4Address, 5354,
/* subtype= */ "ABCDE", /* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"), TEST_TTL), Collections.singletonMap("key", "value"), TEST_TTL),
/* interfaceIndex= */ 20, mockNetwork); /* interfaceIndex= */ 20, mockNetwork);
// Verify onServiceNameDiscovered was called once for the initial response. // Verify onServiceNameDiscovered was called once for the initial response.
@@ -563,9 +565,9 @@ public class MdnsServiceTypeClientTests {
// Process a second response with a different port and updated text attributes. // Process a second response with a different port and updated text attributes.
client.processResponse(createResponse( client.processResponse(createResponse(
"service-instance-1", ipV6Address, 5354, "service-instance-1", ipV6Address, 5354,
/* subtype= */ "ABCDE", /* subtype= */ "ABCDE",
Collections.singletonMap("key", "value"), TEST_TTL), Collections.singletonMap("key", "value"), TEST_TTL),
/* interfaceIndex= */ 20, mockNetwork); /* interfaceIndex= */ 20, mockNetwork);
// Verify onServiceNameDiscovered was called once for the initial response. // Verify onServiceNameDiscovered was called once for the initial response.
@@ -709,7 +711,7 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, mockNetwork, mockSharedLog) { mockDecoderClock, socketKey, mockSharedLog) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -750,7 +752,7 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, mockNetwork, mockSharedLog) { mockDecoderClock, socketKey, mockSharedLog) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -783,7 +785,7 @@ public class MdnsServiceTypeClientTests {
final String serviceInstanceName = "service-instance-1"; final String serviceInstanceName = "service-instance-1";
client = client =
new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, mockNetwork, mockSharedLog) { mockDecoderClock, socketKey, mockSharedLog) {
@Override @Override
MdnsPacketWriter createMdnsPacketWriter() { MdnsPacketWriter createMdnsPacketWriter() {
return mockPacketWriter; return mockPacketWriter;
@@ -835,8 +837,8 @@ public class MdnsServiceTypeClientTests {
// Process the last response which is goodbye message (with the main type, not subtype). // Process the last response which is goodbye message (with the main type, not subtype).
client.processResponse(createResponse( client.processResponse(createResponse(
serviceName, ipV6Address, 5354, SERVICE_TYPE_LABELS, serviceName, ipV6Address, 5354, SERVICE_TYPE_LABELS,
Collections.singletonMap("key", "value"), /* ptrTtlMillis= */ 0L), Collections.singletonMap("key", "value"), /* ptrTtlMillis= */ 0L),
INTERFACE_INDEX, mockNetwork); INTERFACE_INDEX, mockNetwork);
// Verify onServiceNameDiscovered was first called for the initial response. // Verify onServiceNameDiscovered was first called for the initial response.
@@ -908,7 +910,7 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_Resolve() throws Exception { public void testProcessResponse_Resolve() throws Exception {
client = new MdnsServiceTypeClient( client = new MdnsServiceTypeClient(
SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog); SERVICE_TYPE, mockSocketClient, currentThreadExecutor, socketKey, mockSharedLog);
final String instanceName = "service-instance"; final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "}; final String[] hostname = new String[] { "testhost "};
@@ -998,7 +1000,7 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testRenewTxtSrvInResolve() throws Exception { public void testRenewTxtSrvInResolve() throws Exception {
client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, client = new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor,
mockDecoderClock, mockNetwork, mockSharedLog); mockDecoderClock, socketKey, mockSharedLog);
final String instanceName = "service-instance"; final String instanceName = "service-instance";
final String[] hostname = new String[] { "testhost "}; final String[] hostname = new String[] { "testhost "};
@@ -1102,7 +1104,7 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_ResolveExcludesOtherServices() { public void testProcessResponse_ResolveExcludesOtherServices() {
client = new MdnsServiceTypeClient( client = new MdnsServiceTypeClient(
SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog); SERVICE_TYPE, mockSocketClient, currentThreadExecutor, socketKey, mockSharedLog);
final String requestedInstance = "instance1"; final String requestedInstance = "instance1";
final String otherInstance = "instance2"; final String otherInstance = "instance2";
@@ -1119,13 +1121,13 @@ public class MdnsServiceTypeClientTests {
// Complete response from instanceName // Complete response from instanceName
client.processResponse(createResponse( client.processResponse(createResponse(
requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS, requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL), Collections.emptyMap() /* textAttributes */, TEST_TTL),
INTERFACE_INDEX, mockNetwork); INTERFACE_INDEX, mockNetwork);
// Complete response from otherInstanceName // Complete response from otherInstanceName
client.processResponse(createResponse( client.processResponse(createResponse(
otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS, otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
Collections.emptyMap() /* textAttributes */, TEST_TTL), Collections.emptyMap() /* textAttributes */, TEST_TTL),
INTERFACE_INDEX, mockNetwork); INTERFACE_INDEX, mockNetwork);
@@ -1166,7 +1168,7 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() { public void testProcessResponse_SubtypeDiscoveryLimitedToSubtype() {
client = new MdnsServiceTypeClient( client = new MdnsServiceTypeClient(
SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog); SERVICE_TYPE, mockSocketClient, currentThreadExecutor, socketKey, mockSharedLog);
final String matchingInstance = "instance1"; final String matchingInstance = "instance1";
final String subtype = "_subtype"; final String subtype = "_subtype";
@@ -1247,7 +1249,7 @@ public class MdnsServiceTypeClientTests {
@Test @Test
public void testNotifySocketDestroyed() throws Exception { public void testNotifySocketDestroyed() throws Exception {
client = new MdnsServiceTypeClient( client = new MdnsServiceTypeClient(
SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork, mockSharedLog); SERVICE_TYPE, mockSocketClient, currentThreadExecutor, socketKey, mockSharedLog);
final String requestedInstance = "instance1"; final String requestedInstance = "instance1";
final String otherInstance = "instance2"; final String otherInstance = "instance2";

View File

@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -370,7 +371,7 @@ public class MdnsSocketClientTests {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
verify(mockCallback, timeout(TIMEOUT).atLeast(1)) verify(mockCallback, timeout(TIMEOUT).atLeast(1))
.onResponseReceived(any(MdnsPacket.class), anyInt(), any()); .onResponseReceived(any(MdnsPacket.class), any(SocketKey.class));
} }
@Test @Test
@@ -379,7 +380,7 @@ public class MdnsSocketClientTests {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
verify(mockCallback, timeout(TIMEOUT).atLeastOnce()) verify(mockCallback, timeout(TIMEOUT).atLeastOnce())
.onResponseReceived(any(MdnsPacket.class), anyInt(), any()); .onResponseReceived(any(MdnsPacket.class), any(SocketKey.class));
mdnsClient.stopDiscovery(); mdnsClient.stopDiscovery();
} }
@@ -513,7 +514,7 @@ public class MdnsSocketClientTests {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
verify(mockCallback, timeout(TIMEOUT).atLeastOnce()) verify(mockCallback, timeout(TIMEOUT).atLeastOnce())
.onResponseReceived(any(), eq(21), any()); .onResponseReceived(any(), argThat(key -> key.getInterfaceIndex() == 21));
} }
@Test @Test
@@ -536,6 +537,7 @@ public class MdnsSocketClientTests {
mdnsClient.startDiscovery(); mdnsClient.startDiscovery();
verify(mockMulticastSocket, never()).getInterfaceIndex(); verify(mockMulticastSocket, never()).getInterfaceIndex();
verify(mockCallback, timeout(TIMEOUT).atLeast(1)).onResponseReceived(any(), eq(-1), any()); verify(mockCallback, timeout(TIMEOUT).atLeast(1))
.onResponseReceived(any(), argThat(key -> key.getInterfaceIndex() == -1));
} }
} }