Merge "Handle downstream tethering interface addresses in MdnsSocketProvider" am: 885854c9ee

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

Change-Id: I1052d86478a5caf947369c7ca1bc14e2d065a966
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Yuyang Huang
2023-04-16 01:32:49 +00:00
committed by Automerger Merge Worker
6 changed files with 324 additions and 42 deletions

View File

@@ -487,17 +487,23 @@ public class LinkAddress implements Parcelable {
*/ */
@SystemApi @SystemApi
public boolean isGlobalPreferred() { public boolean isGlobalPreferred() {
/**
* Note that addresses flagged as IFA_F_OPTIMISTIC are
* simultaneously flagged as IFA_F_TENTATIVE (when the tentative
* state has cleared either DAD has succeeded or failed, and both
* flags are cleared regardless).
*/
int flags = getFlags();
return (scope == RT_SCOPE_UNIVERSE return (scope == RT_SCOPE_UNIVERSE
&& !isIpv6ULA() && !isIpv6ULA()
&& (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L && isPreferred());
&& ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L)); }
/**
* Checks if the address is a preferred address.
*
* @hide
*/
public boolean isPreferred() {
// Note that addresses flagged as IFA_F_OPTIMISTIC are simultaneously flagged as
// IFA_F_TENTATIVE (when the tentative state has cleared either DAD has succeeded or
// failed, and both flags are cleared regardless).
int flags = getFlags();
return (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
&& ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L);
} }
/** /**

View File

@@ -1330,6 +1330,9 @@ public class NsdService extends INsdManager.Stub {
mDeps = deps; mDeps = deps;
mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper()); mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
// Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
// address events are received.
handler.post(mMdnsSocketProvider::startNetLinkMonitor);
mMdnsSocketClient = mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider); new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager = mMdnsDiscoveryManager =

View File

@@ -36,10 +36,12 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult; import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsLogger; import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException; import java.io.IOException;
@@ -65,6 +67,7 @@ public class MdnsSocketProvider {
// Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames. // Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
private static final int READ_BUFFER_SIZE = 2048; private static final int READ_BUFFER_SIZE = 2048;
private static final MdnsLogger LOGGER = new MdnsLogger(TAG); private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
private static final int IFACE_IDX_NOT_EXIST = -1;
@NonNull private final Context mContext; @NonNull private final Context mContext;
@NonNull private final Looper mLooper; @NonNull private final Looper mLooper;
@NonNull private final Handler mHandler; @NonNull private final Handler mHandler;
@@ -81,6 +84,9 @@ public class MdnsSocketProvider {
new ArrayMap<>(); new ArrayMap<>();
private final List<String> mLocalOnlyInterfaces = new ArrayList<>(); private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
private final List<String> mTetheredInterfaces = new ArrayList<>(); private final List<String> mTetheredInterfaces = new ArrayList<>();
// mIfaceIdxToLinkProperties should not be cleared in maybeStopMonitoringSockets() because
// the netlink monitor is never stop and the old states must be kept.
private final SparseArray<LinkProperties> mIfaceIdxToLinkProperties = new SparseArray<>();
private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE]; private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
private boolean mMonitoringSockets = false; private boolean mMonitoringSockets = false;
private boolean mRequestStop = false; private boolean mRequestStop = false;
@@ -126,8 +132,8 @@ public class MdnsSocketProvider {
} }
}; };
mSocketNetlinkMonitor = SocketNetLinkMonitorFactory.createNetLinkMonitor(mHandler, mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler, LOGGER.mLog,
LOGGER.mLog); new NetLinkMessageProcessor());
} }
/** /**
@@ -148,8 +154,83 @@ public class MdnsSocketProvider {
@NonNull byte[] packetReadBuffer) throws IOException { @NonNull byte[] packetReadBuffer) throws IOException {
return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer); return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
} }
}
/*** Get network interface by given interface name */
public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName) {
final NetworkInterface iface;
try {
iface = NetworkInterface.getByName(ifaceName);
} catch (SocketException e) {
Log.e(TAG, "Error querying interface", e);
return IFACE_IDX_NOT_EXIST;
}
if (iface == null) {
Log.e(TAG, "Interface not found: " + ifaceName);
return IFACE_IDX_NOT_EXIST;
}
return iface.getIndex();
}
/*** Creates a SocketNetlinkMonitor */
public ISocketNetLinkMonitor createSocketNetlinkMonitor(@NonNull final Handler handler,
@NonNull final SharedLog log,
@NonNull final NetLinkMonitorCallBack cb) {
return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
}
}
/**
* The callback interface for the netlink monitor messages.
*/
public interface NetLinkMonitorCallBack {
/**
* Handles the interface address add or update.
*/
void addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull LinkAddress newAddress);
/**
* Handles the interface address delete.
*/
void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress);
}
private class NetLinkMessageProcessor implements NetLinkMonitorCallBack {
@Override
public void addOrUpdateInterfaceAddress(int ifaceIdx,
@NonNull final LinkAddress newAddress) {
LinkProperties linkProperties;
linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
if (linkProperties == null) {
linkProperties = new LinkProperties();
mIfaceIdxToLinkProperties.put(ifaceIdx, linkProperties);
}
boolean updated = linkProperties.addLinkAddress(newAddress);
if (!updated) {
return;
}
maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
}
@Override
public void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress) {
LinkProperties linkProperties;
boolean updated = false;
linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
if (linkProperties != null) {
updated = linkProperties.removeLinkAddress(deleteAddress);
if (linkProperties.getLinkAddresses().isEmpty()) {
mIfaceIdxToLinkProperties.remove(ifaceIdx);
}
}
if (linkProperties == null || !updated) {
return;
}
maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
}
}
/*** Data class for storing socket related info */ /*** Data class for storing socket related info */
private static class SocketInfo { private static class SocketInfo {
final MdnsInterfaceSocket mSocket; final MdnsInterfaceSocket mSocket;
@@ -190,6 +271,15 @@ public class MdnsSocketProvider {
} }
mMonitoringSockets = true; mMonitoringSockets = true;
} }
/**
* Start netlink monitor.
*/
public void startNetLinkMonitor() {
ensureRunningOnHandlerThread(mHandler);
if (mSocketNetlinkMonitor.isSupported()) {
mSocketNetlinkMonitor.startMonitoring();
}
}
private void maybeStopMonitoringSockets() { private void maybeStopMonitoringSockets() {
if (!mMonitoringSockets) return; // Already unregistered. if (!mMonitoringSockets) return; // Already unregistered.
@@ -203,10 +293,6 @@ public class MdnsSocketProvider {
final TetheringManager tetheringManager = mContext.getSystemService( final TetheringManager tetheringManager = mContext.getSystemService(
TetheringManager.class); TetheringManager.class);
tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback); tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
if (mSocketNetlinkMonitor.isSupported()) {
mHandler.post(mSocketNetlinkMonitor::stopMonitoring);
}
// Clear all saved status. // Clear all saved status.
mActiveNetworksLinkProperties.clear(); mActiveNetworksLinkProperties.clear();
mNetworkSockets.clear(); mNetworkSockets.clear();
@@ -215,6 +301,8 @@ public class MdnsSocketProvider {
mTetheredInterfaces.clear(); mTetheredInterfaces.clear();
mMonitoringSockets = false; mMonitoringSockets = false;
} }
// The netlink monitor is not stopped here because the MdnsSocketProvider need to listen
// to all the netlink updates when the system is up and running.
} }
/*** Request to stop monitoring sockets and unregister callbacks */ /*** Request to stop monitoring sockets and unregister callbacks */
@@ -259,21 +347,38 @@ public class MdnsSocketProvider {
if (socketInfo == null) { if (socketInfo == null) {
createSocket(networkKey, lp); createSocket(networkKey, lp);
} else { } else {
// Update the addresses of this socket. updateSocketInfoAddress(network, socketInfo, lp.getLinkAddresses());
final List<LinkAddress> addresses = lp.getLinkAddresses(); }
socketInfo.mAddresses.clear(); }
socketInfo.mAddresses.addAll(addresses); private void maybeUpdateTetheringSocketAddress(int ifaceIndex,
// Try to join the group again. @NonNull final List<LinkAddress> updatedAddresses) {
socketInfo.mSocket.joinGroup(addresses); for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
notifyAddressesChanged(network, socketInfo.mSocket, lp); if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
== ifaceIndex) {
updateSocketInfoAddress(null /* network */,
mTetherInterfaceSockets.valueAt(i), updatedAddresses);
return;
}
} }
} }
private static LinkProperties createLPForTetheredInterface(String interfaceName) { private void updateSocketInfoAddress(@Nullable final Network network,
final LinkProperties linkProperties = new LinkProperties(); @NonNull final SocketInfo socketInfo,
@NonNull final List<LinkAddress> addresses) {
// Update the addresses of this socket.
socketInfo.mAddresses.clear();
socketInfo.mAddresses.addAll(addresses);
// Try to join the group again.
socketInfo.mSocket.joinGroup(addresses);
notifyAddressesChanged(network, socketInfo.mSocket, addresses);
}
private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
int ifaceIndex) {
final LinkProperties linkProperties =
new LinkProperties(mIfaceIdxToLinkProperties.get(ifaceIndex));
linkProperties.setInterfaceName(interfaceName); linkProperties.setInterfaceName(interfaceName);
// TODO: Use NetlinkMonitor to update addresses for tethering interfaces.
return linkProperties; return linkProperties;
} }
@@ -292,7 +397,8 @@ public class MdnsSocketProvider {
final CompareResult<String> interfaceDiff = new CompareResult<>( final CompareResult<String> interfaceDiff = new CompareResult<>(
current, updated); current, updated);
for (String name : interfaceDiff.added) { for (String name : interfaceDiff.added) {
createSocket(LOCAL_NET, createLPForTetheredInterface(name)); int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
} }
for (String name : interfaceDiff.removed) { for (String name : interfaceDiff.removed) {
removeTetherInterfaceSocket(name); removeTetherInterfaceSocket(name);
@@ -332,14 +438,10 @@ public class MdnsSocketProvider {
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket( final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper, networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer); mPacketReadBuffer);
final List<LinkAddress> addresses; final List<LinkAddress> addresses = lp.getLinkAddresses();
if (networkKey == LOCAL_NET) { if (networkKey == LOCAL_NET) {
addresses = CollectionUtils.map(
networkInterface.getInterfaceAddresses(),
i -> new LinkAddress(i.getAddress(), i.getNetworkPrefixLength()));
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses)); mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
} else { } else {
addresses = lp.getLinkAddresses();
mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork, mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
new SocketInfo(socket, addresses)); new SocketInfo(socket, addresses));
} }
@@ -422,12 +524,12 @@ public class MdnsSocketProvider {
} }
private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket, private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
LinkProperties lp) { List<LinkAddress> addresses) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) { for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i); final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) { if (isNetworkMatched(requestedNetwork, network)) {
mCallbacksToRequestedNetworks.keyAt(i) mCallbacksToRequestedNetworks.keyAt(i)
.onAddressesChanged(network, socket, lp.getLinkAddresses()); .onAddressesChanged(network, socket, addresses);
} }
} }
} }
@@ -451,7 +553,10 @@ public class MdnsSocketProvider {
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) { private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName); final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
if (socketInfo == null) { if (socketInfo == null) {
createSocket(LOCAL_NET, createLPForTetheredInterface(interfaceName)); int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
createSocket(
LOCAL_NET,
createLPForTetheredInterface(interfaceName, ifaceIndex));
} else { } else {
// Notify the socket for requested network. // Notify the socket for requested network.
cb.onSocketCreated( cb.onSocketCreated(

View File

@@ -31,8 +31,8 @@ public class SocketNetLinkMonitorFactory {
* Creates a new netlink monitor. * Creates a new netlink monitor.
*/ */
public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler, public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
@NonNull SharedLog log) { @NonNull SharedLog log, @NonNull MdnsSocketProvider.NetLinkMonitorCallBack cb) {
return new SocketNetlinkMonitor(handler, log); return new SocketNetlinkMonitor(handler, log, cb);
} }
private SocketNetLinkMonitorFactory() { private SocketNetLinkMonitorFactory() {

View File

@@ -17,28 +17,64 @@
package com.android.server.connectivity.mdns.internal; package com.android.server.connectivity.mdns.internal;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.net.LinkAddress;
import android.os.Handler; import android.os.Handler;
import android.system.OsConstants; import android.system.OsConstants;
import android.util.Log;
import com.android.net.module.util.SharedLog; import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.NetlinkMonitor; import com.android.net.module.util.ip.NetlinkMonitor;
import com.android.net.module.util.netlink.NetlinkConstants; import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkMessage; import com.android.net.module.util.netlink.NetlinkMessage;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.net.module.util.netlink.StructIfaddrMsg;
import com.android.server.connectivity.mdns.ISocketNetLinkMonitor; import com.android.server.connectivity.mdns.ISocketNetLinkMonitor;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
/** /**
* The netlink monitor for MdnsSocketProvider. * The netlink monitor for MdnsSocketProvider.
*/ */
public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor { public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
public SocketNetlinkMonitor(@NonNull final Handler handler, @NonNull SharedLog log) { public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
super(handler, log, SocketNetlinkMonitor.class.getSimpleName(), OsConstants.NETLINK_ROUTE,
NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
}
@NonNull
private final MdnsSocketProvider.NetLinkMonitorCallBack mCb;
public SocketNetlinkMonitor(@NonNull final Handler handler,
@NonNull SharedLog log,
@NonNull final MdnsSocketProvider.NetLinkMonitorCallBack cb) {
super(handler, log, TAG, OsConstants.NETLINK_ROUTE,
NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
mCb = cb;
}
@Override @Override
public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
if (nlMsg instanceof RtNetlinkAddressMessage) {
processRtNetlinkAddressMessage((RtNetlinkAddressMessage) nlMsg);
}
}
/**
* Process the RTM_NEWADDR and RTM_DELADDR netlink message.
*/
private void processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg) {
final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
msg.getFlags(), ifaddrMsg.scope);
if (!la.isPreferred()) {
// Skip the unusable ip address.
return;
}
switch (msg.getHeader().nlmsg_type) {
case NetlinkConstants.RTM_NEWADDR:
mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
break;
case NetlinkConstants.RTM_DELADDR:
mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
break;
default:
Log.e(TAG, "Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
}
} }
@Override @Override

View File

@@ -21,6 +21,8 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static com.android.testutils.ContextUtils.mockService; import static com.android.testutils.ContextUtils.mockService;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -49,9 +52,19 @@ import android.net.TetheringManager.TetheringEventCallback;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.system.OsConstants;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.net.module.util.ArrayTrackRecord; import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.net.module.util.netlink.StructIfaddrMsg;
import com.android.net.module.util.netlink.StructNlMsgHdr;
import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies; import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies;
import com.android.server.connectivity.mdns.internal.SocketNetlinkMonitor;
import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner; import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils; import com.android.testutils.HandlerUtils;
@@ -64,20 +77,28 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.io.IOException; import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@RunWith(DevSdkIgnoreRunner.class) @RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class MdnsSocketProviderTest { public class MdnsSocketProviderTest {
private static final String TAG = MdnsSocketProviderTest.class.getSimpleName();
private static final String TEST_IFACE_NAME = "test"; private static final String TEST_IFACE_NAME = "test";
private static final String LOCAL_ONLY_IFACE_NAME = "local_only"; private static final String LOCAL_ONLY_IFACE_NAME = "local_only";
private static final String TETHERED_IFACE_NAME = "tethered"; private static final String TETHERED_IFACE_NAME = "tethered";
private static final int TETHERED_IFACE_IDX = 32;
private static final long DEFAULT_TIMEOUT = 2000L; private static final long DEFAULT_TIMEOUT = 2000L;
private static final long NO_CALLBACK_TIMEOUT = 200L; private static final long NO_CALLBACK_TIMEOUT = 200L;
private static final LinkAddress LINKADDRV4 = new LinkAddress("192.0.2.0/24"); private static final LinkAddress LINKADDRV4 = new LinkAddress("192.0.2.0/24");
private static final LinkAddress LINKADDRV6 = private static final LinkAddress LINKADDRV6 =
new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"); new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64");
private static final LinkAddress LINKADDRV6_FLAG_CHANGE =
new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", 1 /* flags */,
0 /* scope */);
private static final Network TEST_NETWORK = new Network(123); private static final Network TEST_NETWORK = new Network(123);
@Mock private Context mContext; @Mock private Context mContext;
@Mock private Dependencies mDeps; @Mock private Dependencies mDeps;
@@ -91,6 +112,7 @@ public class MdnsSocketProviderTest {
private NetworkCallback mNetworkCallback; private NetworkCallback mNetworkCallback;
private TetheringEventCallback mTetheringEventCallback; private TetheringEventCallback mTetheringEventCallback;
private TestNetLinkMonitor mTestSocketNetLinkMonitor;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -116,9 +138,21 @@ public class MdnsSocketProviderTest {
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME); doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
doReturn(mock(MdnsInterfaceSocket.class)) doReturn(mock(MdnsInterfaceSocket.class))
.when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any()); .when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
TETHERED_IFACE_NAME);
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest"); final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start(); thread.start();
mHandler = new Handler(thread.getLooper()); mHandler = new Handler(thread.getLooper());
doReturn(mTestSocketNetLinkMonitor).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
doAnswer(inv -> {
mTestSocketNetLinkMonitor = new TestNetLinkMonitor(inv.getArgument(0),
inv.getArgument(1),
inv.getArgument(2));
return mTestSocketNetLinkMonitor;
}).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps); mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps);
} }
@@ -135,6 +169,23 @@ public class MdnsSocketProviderTest {
mNetworkCallback = nwCallbackCaptor.getValue(); mNetworkCallback = nwCallbackCaptor.getValue();
mTetheringEventCallback = teCallbackCaptor.getValue(); mTetheringEventCallback = teCallbackCaptor.getValue();
mHandler.post(mSocketProvider::startNetLinkMonitor);
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
}
private static class TestNetLinkMonitor extends SocketNetlinkMonitor {
TestNetLinkMonitor(@NonNull Handler handler,
@NonNull SharedLog log,
@Nullable MdnsSocketProvider.NetLinkMonitorCallBack cb) {
super(handler, log, cb);
}
@Override
public void startMonitoring() { }
@Override
public void stopMonitoring() { }
} }
private class TestSocketCallback implements MdnsSocketProvider.SocketCallback { private class TestSocketCallback implements MdnsSocketProvider.SocketCallback {
@@ -301,6 +352,87 @@ public class MdnsSocketProviderTest {
testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */); testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
} }
private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(
short msgType, LinkAddress linkAddress, int ifIndex, int flags) {
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_type = msgType;
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlmsghdr.nlmsg_seq = 1;
InetAddress ip = linkAddress.getAddress();
final byte family =
(byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
StructIfaddrMsg structIfaddrMsg = new StructIfaddrMsg(family,
(short) linkAddress.getPrefixLength(),
(short) linkAddress.getFlags(), (short) linkAddress.getScope(), ifIndex);
return new RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg, ip,
null /* structIfacacheInfo */, flags);
}
@Test
public void testDownstreamNetworkAddressUpdateFromNetlink() {
startMonitoringSockets();
final TestSocketCallback testCallbackAll = new TestSocketCallback();
mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// Address add message arrived before the interface is created.
RtNetlinkAddressMessage addIpv4AddrMsg = createNetworkAddressUpdateNetLink(
NetlinkConstants.RTM_NEWADDR,
LINKADDRV4,
TETHERED_IFACE_IDX,
0 /* flags */);
mHandler.post(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv4AddrMsg,
0 /* whenMs */));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// Interface is created.
mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
List.of(TETHERED_IFACE_NAME)));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mTetheredIfaceWrapper).getNetworkInterface();
testCallbackAll.expectedSocketCreatedForNetwork(null /* network */, List.of(LINKADDRV4));
// Old Address removed.
RtNetlinkAddressMessage removeIpv4AddrMsg = createNetworkAddressUpdateNetLink(
NetlinkConstants.RTM_DELADDR,
LINKADDRV4,
TETHERED_IFACE_IDX,
0 /* flags */);
mHandler.post(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(removeIpv4AddrMsg,
0 /* whenMs */));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of());
// New address added.
RtNetlinkAddressMessage addIpv6AddrMsg = createNetworkAddressUpdateNetLink(
NetlinkConstants.RTM_NEWADDR,
LINKADDRV6,
TETHERED_IFACE_IDX,
0 /* flags */);
mHandler.post(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
0 /* whenMs */));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of(LINKADDRV6));
// Address updated
RtNetlinkAddressMessage updateIpv6AddrMsg = createNetworkAddressUpdateNetLink(
NetlinkConstants.RTM_NEWADDR,
LINKADDRV6,
TETHERED_IFACE_IDX,
1 /* flags */);
mHandler.post(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(updateIpv6AddrMsg,
0 /* whenMs */));
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */,
List.of(LINKADDRV6_FLAG_CHANGE));
}
@Test @Test
public void testAddressesChanged() throws Exception { public void testAddressesChanged() throws Exception {
startMonitoringSockets(); startMonitoringSockets();