Handle downstream tethering interface addresses in MdnsSocketProvider
MdnsSocketProvider currently does not fill addresses of downstream
tethering interfaces in its callbacks. The interface addresses should be
properly updated by listening to the Netlink messages.
Test: atest FrameworksNetTests CtsNetTestCases
Bug: 267980538
Change-Id: I753e547a1b092703fe59c6c9e922ee8aca245f67
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
index d48b8c7..90f55b3 100644
--- a/framework/src/android/net/LinkAddress.java
+++ b/framework/src/android/net/LinkAddress.java
@@ -487,17 +487,23 @@
*/
@SystemApi
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
&& !isIpv6ULA()
- && (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
- && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L));
+ && isPreferred());
+ }
+
+ /**
+ * 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);
}
/**
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index a658791..c5104d8 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1330,6 +1330,9 @@
mDeps = deps;
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 =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager =
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 0952e88..8017ee0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -36,10 +36,12 @@
import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException;
@@ -65,6 +67,7 @@
// Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
private static final int READ_BUFFER_SIZE = 2048;
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 Looper mLooper;
@NonNull private final Handler mHandler;
@@ -81,6 +84,9 @@
new ArrayMap<>();
private final List<String> mLocalOnlyInterfaces = 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 boolean mMonitoringSockets = false;
private boolean mRequestStop = false;
@@ -126,8 +132,8 @@
}
};
- mSocketNetlinkMonitor = SocketNetLinkMonitorFactory.createNetLinkMonitor(mHandler,
- LOGGER.mLog);
+ mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler, LOGGER.mLog,
+ new NetLinkMessageProcessor());
}
/**
@@ -148,8 +154,83 @@
@NonNull byte[] packetReadBuffer) throws IOException {
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 */
private static class SocketInfo {
final MdnsInterfaceSocket mSocket;
@@ -190,6 +271,15 @@
}
mMonitoringSockets = true;
}
+ /**
+ * Start netlink monitor.
+ */
+ public void startNetLinkMonitor() {
+ ensureRunningOnHandlerThread(mHandler);
+ if (mSocketNetlinkMonitor.isSupported()) {
+ mSocketNetlinkMonitor.startMonitoring();
+ }
+ }
private void maybeStopMonitoringSockets() {
if (!mMonitoringSockets) return; // Already unregistered.
@@ -203,10 +293,6 @@
final TetheringManager tetheringManager = mContext.getSystemService(
TetheringManager.class);
tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
-
- if (mSocketNetlinkMonitor.isSupported()) {
- mHandler.post(mSocketNetlinkMonitor::stopMonitoring);
- }
// Clear all saved status.
mActiveNetworksLinkProperties.clear();
mNetworkSockets.clear();
@@ -215,6 +301,8 @@
mTetheredInterfaces.clear();
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 */
@@ -259,21 +347,38 @@
if (socketInfo == null) {
createSocket(networkKey, lp);
} else {
- // Update the addresses of this socket.
- final List<LinkAddress> addresses = lp.getLinkAddresses();
- socketInfo.mAddresses.clear();
- socketInfo.mAddresses.addAll(addresses);
- // Try to join the group again.
- socketInfo.mSocket.joinGroup(addresses);
-
- notifyAddressesChanged(network, socketInfo.mSocket, lp);
+ updateSocketInfoAddress(network, socketInfo, lp.getLinkAddresses());
+ }
+ }
+ private void maybeUpdateTetheringSocketAddress(int ifaceIndex,
+ @NonNull final List<LinkAddress> updatedAddresses) {
+ for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
+ String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
+ if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
+ == ifaceIndex) {
+ updateSocketInfoAddress(null /* network */,
+ mTetherInterfaceSockets.valueAt(i), updatedAddresses);
+ return;
+ }
}
}
- private static LinkProperties createLPForTetheredInterface(String interfaceName) {
- final LinkProperties linkProperties = new LinkProperties();
+ private void updateSocketInfoAddress(@Nullable final Network network,
+ @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);
- // TODO: Use NetlinkMonitor to update addresses for tethering interfaces.
return linkProperties;
}
@@ -292,7 +397,8 @@
final CompareResult<String> interfaceDiff = new CompareResult<>(
current, updated);
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) {
removeTetherInterfaceSocket(name);
@@ -332,14 +438,10 @@
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
- final List<LinkAddress> addresses;
+ final List<LinkAddress> addresses = lp.getLinkAddresses();
if (networkKey == LOCAL_NET) {
- addresses = CollectionUtils.map(
- networkInterface.getInterfaceAddresses(),
- i -> new LinkAddress(i.getAddress(), i.getNetworkPrefixLength()));
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
} else {
- addresses = lp.getLinkAddresses();
mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
new SocketInfo(socket, addresses));
}
@@ -422,12 +524,12 @@
}
private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
- LinkProperties lp) {
+ List<LinkAddress> addresses) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) {
mCallbacksToRequestedNetworks.keyAt(i)
- .onAddressesChanged(network, socket, lp.getLinkAddresses());
+ .onAddressesChanged(network, socket, addresses);
}
}
}
@@ -451,7 +553,10 @@
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
if (socketInfo == null) {
- createSocket(LOCAL_NET, createLPForTetheredInterface(interfaceName));
+ int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
+ createSocket(
+ LOCAL_NET,
+ createLPForTetheredInterface(interfaceName, ifaceIndex));
} else {
// Notify the socket for requested network.
cb.onSocketCreated(
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
index 8f6aecc..4650255 100644
--- a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
@@ -31,8 +31,8 @@
* Creates a new netlink monitor.
*/
public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
- @NonNull SharedLog log) {
- return new SocketNetlinkMonitor(handler, log);
+ @NonNull SharedLog log, @NonNull MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+ return new SocketNetlinkMonitor(handler, log, cb);
}
private SocketNetLinkMonitorFactory() {
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index e053413..6395b53 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -17,28 +17,64 @@
package com.android.server.connectivity.mdns.internal;
import android.annotation.NonNull;
+import android.net.LinkAddress;
import android.os.Handler;
import android.system.OsConstants;
+import android.util.Log;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.NetlinkMonitor;
import com.android.net.module.util.netlink.NetlinkConstants;
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.MdnsSocketProvider;
/**
* The netlink monitor for MdnsSocketProvider.
*/
public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
- public SocketNetlinkMonitor(@NonNull final Handler handler, @NonNull SharedLog log) {
- super(handler, log, SocketNetlinkMonitor.class.getSimpleName(), OsConstants.NETLINK_ROUTE,
- NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
- }
+ public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
+ @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
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
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index d9420b8..2d73c98 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -21,6 +21,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
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 org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -49,9 +52,19 @@
import android.os.Build;
import android.os.Handler;
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.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.internal.SocketNetlinkMonitor;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -64,20 +77,28 @@
import org.mockito.MockitoAnnotations;
import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class MdnsSocketProviderTest {
+ private static final String TAG = MdnsSocketProviderTest.class.getSimpleName();
private static final String TEST_IFACE_NAME = "test";
private static final String LOCAL_ONLY_IFACE_NAME = "local_only";
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 NO_CALLBACK_TIMEOUT = 200L;
private static final LinkAddress LINKADDRV4 = new LinkAddress("192.0.2.0/24");
private static final LinkAddress LINKADDRV6 =
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);
@Mock private Context mContext;
@Mock private Dependencies mDeps;
@@ -91,6 +112,7 @@
private NetworkCallback mNetworkCallback;
private TetheringEventCallback mTetheringEventCallback;
+ private TestNetLinkMonitor mTestSocketNetLinkMonitor;
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
@@ -116,9 +138,21 @@
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
doReturn(mock(MdnsInterfaceSocket.class))
.when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
+ doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
+ TETHERED_IFACE_NAME);
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start();
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);
}
@@ -135,6 +169,23 @@
mNetworkCallback = nwCallbackCaptor.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 {
@@ -301,6 +352,87 @@
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
public void testAddressesChanged() throws Exception {
startMonitoringSockets();