diff --git a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java index 9a670070cb..866ecba62a 100644 --- a/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java +++ b/service-t/src/com/android/server/connectivity/mdns/EnqueueMdnsQueryCallable.java @@ -211,7 +211,7 @@ public class EnqueueMdnsQueryCallable implements Callable sendMdnsPacket(packet, network)); } - /** Sends a mDNS request packet that asks for unicast response. */ - @Override - public void sendUnicastPacket(@NonNull DatagramPacket packet) { - sendUnicastPacket(packet, null /* network */); - } - /** * Sends a mDNS request packet via given network that asks for unicast response. Null network * means sending packet via all networks. diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java index 783b18a000..1144d16056 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClient.java @@ -195,13 +195,11 @@ public class MdnsSocketClient implements MdnsSocketClientBase { } /** Sends a mDNS request packet that asks for multicast response. */ - @Override public void sendMulticastPacket(@NonNull DatagramPacket packet) { sendMdnsPacket(packet, multicastPacketQueue); } /** Sends a mDNS request packet that asks for unicast response. */ - @Override public void sendUnicastPacket(DatagramPacket packet) { if (useSeparateSocketForUnicast) { sendMdnsPacket(packet, unicastPacketQueue); @@ -210,6 +208,36 @@ public class MdnsSocketClient implements MdnsSocketClientBase { } } + @Override + public void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { + if (network != null) { + throw new IllegalArgumentException("This socket client does not support sending to " + + "specific networks"); + } + sendMulticastPacket(packet); + } + + @Override + public void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { + if (network != null) { + throw new IllegalArgumentException("This socket client does not support sending to " + + "specific networks"); + } + sendUnicastPacket(packet); + } + + @Override + public void notifyNetworkRequested( + @NonNull MdnsServiceBrowserListener listener, + @Nullable Network network, + @NonNull SocketCreationCallback socketCreationCallback) { + if (network != null) { + throw new IllegalArgumentException("This socket client does not support requesting " + + "specific networks"); + } + socketCreationCallback.onSocketCreated(null); + } + private void sendMdnsPacket(DatagramPacket packet, Queue packetQueueToUse) { if (shouldStopSocketLoop && !MdnsConfigs.allowAddMdnsPacketAfterDiscoveryStops()) { LOGGER.w("sendMdnsPacket() is called after discovery already stopped"); diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java index c614cd3d18..22cf326b2c 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketClientBase.java @@ -38,35 +38,25 @@ public interface MdnsSocketClientBase { /*** Set callback for receiving mDns response */ void setCallback(@Nullable Callback callback); - /*** Sends a mDNS request packet that asks for multicast response. */ - void sendMulticastPacket(@NonNull DatagramPacket packet); + /** + * Send a mDNS request packet via given network that asks for multicast response. + * + *

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

The socket client may use a null network to identify some or all interfaces, in which case + * passing null sends the packet to these. */ - default void sendMulticastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - throw new UnsupportedOperationException( - "This socket client doesn't support per-network sending"); - } - - /*** Sends a mDNS request packet that asks for unicast response. */ - void sendUnicastPacket(@NonNull DatagramPacket packet); - - /** - * Sends a mDNS request packet via given network that asks for unicast response. Null network - * means sending packet via all networks. - */ - default void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network) { - throw new UnsupportedOperationException( - "This socket client doesn't support per-network sending"); - } + void sendUnicastPacket(@NonNull DatagramPacket packet, @Nullable Network network); /*** Notify that the given network is requested for mdns discovery / resolution */ - default void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener, - @Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback) { - socketCreationCallback.onSocketCreated(network); - } + void notifyNetworkRequested(@NonNull MdnsServiceBrowserListener listener, + @Nullable Network network, @NonNull SocketCreationCallback socketCreationCallback); /*** Notify that the network is unrequested */ default void notifyNetworkUnrequested(@NonNull MdnsServiceBrowserListener listener) { } diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java index c6137c61a3..87ba5d73bc 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java @@ -38,6 +38,7 @@ import android.os.Handler; import android.os.HandlerThread; import com.android.net.module.util.HexDump; +import com.android.server.connectivity.mdns.MdnsSocketClientBase.SocketCreationCallback; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import com.android.testutils.HandlerUtils; @@ -66,7 +67,7 @@ public class MdnsMultinetworkSocketClientTest { @Mock private MdnsInterfaceSocket mSocket; @Mock private MdnsServiceBrowserListener mListener; @Mock private MdnsSocketClientBase.Callback mCallback; - @Mock private MdnsSocketClientBase.SocketCreationCallback mSocketCreationCallback; + @Mock private SocketCreationCallback mSocketCreationCallback; private MdnsMultinetworkSocketClient mSocketClient; private Handler mHandler; @@ -113,22 +114,36 @@ public class MdnsMultinetworkSocketClientTest { InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */); final DatagramPacket ipv6Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length, InetAddresses.parseNumericAddress("2001:db8::"), 0 /* port */); - doReturn(true).when(mSocket).hasJoinedIpv4(); - doReturn(true).when(mSocket).hasJoinedIpv6(); - doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); + + final MdnsInterfaceSocket tetherIfaceSock1 = mock(MdnsInterfaceSocket.class); + final MdnsInterfaceSocket tetherIfaceSock2 = mock(MdnsInterfaceSocket.class); + for (MdnsInterfaceSocket socket : List.of(mSocket, tetherIfaceSock1, tetherIfaceSock2)) { + doReturn(true).when(socket).hasJoinedIpv4(); + doReturn(true).when(socket).hasJoinedIpv6(); + doReturn(createEmptyNetworkInterface()).when(socket).getInterface(); + } + // Notify socket created callback.onSocketCreated(mNetwork, mSocket, List.of()); verify(mSocketCreationCallback).onSocketCreated(mNetwork); + callback.onSocketCreated(null, tetherIfaceSock1, List.of()); + verify(mSocketCreationCallback).onSocketCreated(null); + callback.onSocketCreated(null, tetherIfaceSock2, List.of()); + verify(mSocketCreationCallback, times(2)).onSocketCreated(null); // Send packet to IPv4 with target network and verify sending has been called. mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); verify(mSocket).send(ipv4Packet); + verify(tetherIfaceSock1, never()).send(any()); + verify(tetherIfaceSock2, never()).send(any()); // Send packet to IPv6 without target network and verify sending has been called. - mSocketClient.sendMulticastPacket(ipv6Packet); + mSocketClient.sendMulticastPacket(ipv6Packet, null); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); - verify(mSocket).send(ipv6Packet); + verify(mSocket, never()).send(ipv6Packet); + verify(tetherIfaceSock1).send(ipv6Packet); + verify(tetherIfaceSock2).send(ipv6Packet); } @Test @@ -181,61 +196,86 @@ public class MdnsMultinetworkSocketClientTest { @Test public void testSocketRemovedAfterNetworkUnrequested() throws IOException { - // Request a socket - final SocketCallback callback = expectSocketCallback(mListener, mNetwork); + // Request sockets on all networks + final SocketCallback callback = expectSocketCallback(mListener, null); final DatagramPacket ipv4Packet = new DatagramPacket(BUFFER, 0 /* offset */, BUFFER.length, InetAddresses.parseNumericAddress("192.0.2.1"), 0 /* port */); + + // Notify 3 socket created, including 2 tethered interfaces (null network) + final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class); + final MdnsInterfaceSocket socket3 = mock(MdnsInterfaceSocket.class); doReturn(true).when(mSocket).hasJoinedIpv4(); doReturn(true).when(mSocket).hasJoinedIpv6(); - doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); - // Notify socket created - callback.onSocketCreated(mNetwork, mSocket, List.of()); - verify(mSocketCreationCallback).onSocketCreated(mNetwork); - - // Send IPv4 packet and verify sending has been called. - mSocketClient.sendMulticastPacket(ipv4Packet); - HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); - verify(mSocket).send(ipv4Packet); - - // Request another socket with null network - final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class); - final Network network2 = mock(Network.class); - final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class); - final SocketCallback callback2 = expectSocketCallback(listener2, null); doReturn(true).when(socket2).hasJoinedIpv4(); doReturn(true).when(socket2).hasJoinedIpv6(); + doReturn(true).when(socket3).hasJoinedIpv4(); + doReturn(true).when(socket3).hasJoinedIpv6(); + doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface(); doReturn(createEmptyNetworkInterface()).when(socket2).getInterface(); - // Notify socket created for two networks. - callback2.onSocketCreated(mNetwork, mSocket, List.of()); - callback2.onSocketCreated(network2, socket2, List.of()); - verify(mSocketCreationCallback, times(2)).onSocketCreated(mNetwork); - verify(mSocketCreationCallback).onSocketCreated(network2); + doReturn(createEmptyNetworkInterface()).when(socket3).getInterface(); - // Send IPv4 packet and verify sending to two sockets. - mSocketClient.sendMulticastPacket(ipv4Packet); + callback.onSocketCreated(mNetwork, mSocket, List.of()); + callback.onSocketCreated(null, socket2, List.of()); + callback.onSocketCreated(null, socket3, List.of()); + verify(mSocketCreationCallback).onSocketCreated(mNetwork); + verify(mSocketCreationCallback, times(2)).onSocketCreated(null); + + // Send IPv4 packet on the non-null Network and verify sending has been called. + mSocketClient.sendMulticastPacket(ipv4Packet, mNetwork); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); - verify(mSocket, times(2)).send(ipv4Packet); - verify(socket2).send(ipv4Packet); + verify(mSocket).send(ipv4Packet); + verify(socket2, never()).send(any()); + verify(socket3, never()).send(any()); - // Unrequest another socket + // Request another socket with null network, get the same interfaces + final SocketCreationCallback socketCreationCb2 = mock(SocketCreationCallback.class); + final MdnsServiceBrowserListener listener2 = mock(MdnsServiceBrowserListener.class); + + // requestSocket is called a second time + final ArgumentCaptor callback2Captor = + ArgumentCaptor.forClass(SocketCallback.class); + mHandler.post(() -> mSocketClient.notifyNetworkRequested( + listener2, null, socketCreationCb2)); + HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); + verify(mProvider, times(2)).requestSocket(eq(null), callback2Captor.capture()); + final SocketCallback callback2 = callback2Captor.getAllValues().get(1); + + // Notify socket created for all networks. + callback2.onSocketCreated(mNetwork, mSocket, List.of()); + callback2.onSocketCreated(null, socket2, List.of()); + callback2.onSocketCreated(null, socket3, List.of()); + verify(socketCreationCb2).onSocketCreated(mNetwork); + verify(socketCreationCb2, times(2)).onSocketCreated(null); + + // Send IPv4 packet to null network and verify sending to the 2 tethered interface sockets. + mSocketClient.sendMulticastPacket(ipv4Packet, null); + HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); + // ipv4Packet still sent only once on mSocket: times(1) matches the packet sent earlier on + // mNetwork + verify(mSocket, times(1)).send(ipv4Packet); + verify(socket2).send(ipv4Packet); + verify(socket3).send(ipv4Packet); + + // Unregister the second request mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(listener2)); verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback2); - // Send IPv4 packet again and verify only sending via mSocket - mSocketClient.sendMulticastPacket(ipv4Packet); + // Send IPv4 packet again and verify it's still sent a second time + mSocketClient.sendMulticastPacket(ipv4Packet, null); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); - verify(mSocket, times(3)).send(ipv4Packet); - verify(socket2).send(ipv4Packet); + verify(socket2, times(2)).send(ipv4Packet); + verify(socket3, times(2)).send(ipv4Packet); - // Unrequest remaining socket + // Unrequest remaining sockets mHandler.post(() -> mSocketClient.notifyNetworkUnrequested(mListener)); verify(mProvider, timeout(DEFAULT_TIMEOUT)).unrequestSocket(callback); // Send IPv4 packet and verify no more sending. - mSocketClient.sendMulticastPacket(ipv4Packet); + mSocketClient.sendMulticastPacket(ipv4Packet, null); HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT); - verify(mSocket, times(3)).send(ipv4Packet); - verify(socket2).send(ipv4Packet); + verify(mSocket, times(1)).send(ipv4Packet); + verify(socket2, times(2)).send(ipv4Packet); + verify(socket3, times(2)).send(ipv4Packet); } @Test