Merge "Handle downstream tethering interface addresses in MdnsSocketProvider"
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
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 void updateSocketInfoAddress(@Nullable final Network network,
|
||||||
|
@NonNull final SocketInfo socketInfo,
|
||||||
|
@NonNull final List<LinkAddress> addresses) {
|
||||||
// Update the addresses of this socket.
|
// Update the addresses of this socket.
|
||||||
final List<LinkAddress> addresses = lp.getLinkAddresses();
|
|
||||||
socketInfo.mAddresses.clear();
|
socketInfo.mAddresses.clear();
|
||||||
socketInfo.mAddresses.addAll(addresses);
|
socketInfo.mAddresses.addAll(addresses);
|
||||||
// Try to join the group again.
|
// Try to join the group again.
|
||||||
socketInfo.mSocket.joinGroup(addresses);
|
socketInfo.mSocket.joinGroup(addresses);
|
||||||
|
|
||||||
notifyAddressesChanged(network, socketInfo.mSocket, lp);
|
notifyAddressesChanged(network, socketInfo.mSocket, addresses);
|
||||||
}
|
}
|
||||||
}
|
private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
|
||||||
|
int ifaceIndex) {
|
||||||
private static LinkProperties createLPForTetheredInterface(String interfaceName) {
|
final LinkProperties linkProperties =
|
||||||
final LinkProperties linkProperties = new 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(
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user