Merge changes Icd842479,I2206a846
* changes: Add MdnsInterfaceSocket and MdnsSocketProvider Put the Network info in MdnsServiceInfo
This commit is contained in:
@@ -203,6 +203,7 @@ java_library {
|
|||||||
libs: [
|
libs: [
|
||||||
"framework-annotations-lib",
|
"framework-annotations-lib",
|
||||||
"framework-connectivity-pre-jarjar",
|
"framework-connectivity-pre-jarjar",
|
||||||
|
"framework-tethering",
|
||||||
"framework-wifi",
|
"framework-wifi",
|
||||||
"service-connectivity-pre-jarjar",
|
"service-connectivity-pre-jarjar",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
/** Interface for monitoring connectivity changes. */
|
/** Interface for monitoring connectivity changes. */
|
||||||
public interface ConnectivityMonitor {
|
public interface ConnectivityMonitor {
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +31,9 @@ public interface ConnectivityMonitor {
|
|||||||
|
|
||||||
void notifyConnectivityChange();
|
void notifyConnectivityChange();
|
||||||
|
|
||||||
|
/** Get available network which is received from connectivity change. */
|
||||||
|
Network getAvailableNetwork();
|
||||||
|
|
||||||
/** Listener interface for receiving connectivity changes. */
|
/** Listener interface for receiving connectivity changes. */
|
||||||
interface Listener {
|
interface Listener {
|
||||||
void onConnectivityChanged();
|
void onConnectivityChanged();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
@@ -37,6 +38,7 @@ public class ConnectivityMonitorWithConnectivityManager implements ConnectivityM
|
|||||||
// TODO(b/71901993): Ideally we shouldn't need this flag. However we still don't have clues why
|
// TODO(b/71901993): Ideally we shouldn't need this flag. However we still don't have clues why
|
||||||
// the receiver is unregistered twice yet.
|
// the receiver is unregistered twice yet.
|
||||||
private boolean isCallbackRegistered = false;
|
private boolean isCallbackRegistered = false;
|
||||||
|
private Network lastAvailableNetwork = null;
|
||||||
|
|
||||||
@SuppressWarnings({"nullness:assignment", "nullness:method.invocation"})
|
@SuppressWarnings({"nullness:assignment", "nullness:method.invocation"})
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
@@ -50,6 +52,7 @@ public class ConnectivityMonitorWithConnectivityManager implements ConnectivityM
|
|||||||
@Override
|
@Override
|
||||||
public void onAvailable(Network network) {
|
public void onAvailable(Network network) {
|
||||||
LOGGER.log("network available.");
|
LOGGER.log("network available.");
|
||||||
|
lastAvailableNetwork = network;
|
||||||
notifyConnectivityChange();
|
notifyConnectivityChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,4 +106,10 @@ public class ConnectivityMonitorWithConnectivityManager implements ConnectivityM
|
|||||||
connectivityManager.unregisterNetworkCallback(networkCallback);
|
connectivityManager.unregisterNetworkCallback(networkCallback);
|
||||||
isCallbackRegistered = false;
|
isCallbackRegistered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Network getAvailableNetwork() {
|
||||||
|
return lastAvailableNetwork;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
||||||
@@ -34,7 +35,8 @@ import java.util.Map;
|
|||||||
* notify them when a mDNS service instance is found, updated, or removed?
|
* notify them when a mDNS service instance is found, updated, or removed?
|
||||||
*/
|
*/
|
||||||
public class MdnsDiscoveryManager implements MdnsSocketClient.Callback {
|
public class MdnsDiscoveryManager implements MdnsSocketClient.Callback {
|
||||||
|
private static final String TAG = MdnsDiscoveryManager.class.getSimpleName();
|
||||||
|
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsDiscoveryManager");
|
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsDiscoveryManager");
|
||||||
|
|
||||||
private final ExecutorProvider executorProvider;
|
private final ExecutorProvider executorProvider;
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSocket.MULTICAST_IPV4_ADDRESS;
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSocket.MULTICAST_IPV6_ADDRESS;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.util.SocketUtils;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.MulticastSocket;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MdnsInterfaceSocket} provides a similar interface to {@link MulticastSocket} and binds to
|
||||||
|
* an available multicast network interfaces.
|
||||||
|
*
|
||||||
|
* <p>This isn't thread safe and should be always called on the same thread unless specified
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @see MulticastSocket for javadoc of each public method.
|
||||||
|
*/
|
||||||
|
public class MdnsInterfaceSocket {
|
||||||
|
private static final String TAG = MdnsInterfaceSocket.class.getSimpleName();
|
||||||
|
@NonNull private final MulticastSocket mMulticastSocket;
|
||||||
|
@NonNull private final NetworkInterface mNetworkInterface;
|
||||||
|
private boolean mJoinedIpv4 = false;
|
||||||
|
private boolean mJoinedIpv6 = false;
|
||||||
|
|
||||||
|
public MdnsInterfaceSocket(@NonNull NetworkInterface networkInterface, int port)
|
||||||
|
throws IOException {
|
||||||
|
mNetworkInterface = networkInterface;
|
||||||
|
mMulticastSocket = new MulticastSocket(port);
|
||||||
|
// RFC Spec: https://tools.ietf.org/html/rfc6762. Time to live is set 255
|
||||||
|
mMulticastSocket.setTimeToLive(255);
|
||||||
|
mMulticastSocket.setNetworkInterface(networkInterface);
|
||||||
|
|
||||||
|
// Bind socket to the interface for receiving from that interface only.
|
||||||
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(mMulticastSocket)) {
|
||||||
|
SocketUtils.bindSocketToInterface(pfd.getFileDescriptor(), mNetworkInterface.getName());
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
throw new IOException("Error setting socket options", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a datagram packet from this socket.
|
||||||
|
*
|
||||||
|
* <p>This method could be used on any thread.
|
||||||
|
*/
|
||||||
|
public void send(@NonNull DatagramPacket packet) throws IOException {
|
||||||
|
mMulticastSocket.send(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives a datagram packet from this socket.
|
||||||
|
*
|
||||||
|
* <p>This method could be used on any thread.
|
||||||
|
*/
|
||||||
|
public void receive(@NonNull DatagramPacket packet) throws IOException {
|
||||||
|
mMulticastSocket.receive(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasIpv4Address(List<LinkAddress> addresses) {
|
||||||
|
for (LinkAddress address : addresses) {
|
||||||
|
if (address.isIpv4()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasIpv6Address(List<LinkAddress> addresses) {
|
||||||
|
for (LinkAddress address : addresses) {
|
||||||
|
if (address.isIpv6()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Joins both IPv4 and IPv6 multicast groups. */
|
||||||
|
public void joinGroup(@NonNull List<LinkAddress> addresses) {
|
||||||
|
maybeJoinIpv4(addresses);
|
||||||
|
maybeJoinIpv6(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean joinGroup(InetSocketAddress multicastAddress) {
|
||||||
|
try {
|
||||||
|
mMulticastSocket.joinGroup(multicastAddress, mNetworkInterface);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// The address may have just been removed
|
||||||
|
Log.e(TAG, "Error joining multicast group for " + mNetworkInterface, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeJoinIpv4(List<LinkAddress> addresses) {
|
||||||
|
final boolean hasAddr = hasIpv4Address(addresses);
|
||||||
|
if (!mJoinedIpv4 && hasAddr) {
|
||||||
|
mJoinedIpv4 = joinGroup(MULTICAST_IPV4_ADDRESS);
|
||||||
|
} else if (!hasAddr) {
|
||||||
|
// Lost IPv4 address
|
||||||
|
mJoinedIpv4 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeJoinIpv6(List<LinkAddress> addresses) {
|
||||||
|
final boolean hasAddr = hasIpv6Address(addresses);
|
||||||
|
if (!mJoinedIpv6 && hasAddr) {
|
||||||
|
mJoinedIpv6 = joinGroup(MULTICAST_IPV6_ADDRESS);
|
||||||
|
} else if (!hasAddr) {
|
||||||
|
// Lost IPv6 address
|
||||||
|
mJoinedIpv6 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Destroy this socket by leaving all joined multicast groups and closing this socket. */
|
||||||
|
public void destroy() {
|
||||||
|
if (mJoinedIpv4) {
|
||||||
|
try {
|
||||||
|
mMulticastSocket.leaveGroup(MULTICAST_IPV4_ADDRESS, mNetworkInterface);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error leaving IPv4 group for " + mNetworkInterface, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mJoinedIpv6) {
|
||||||
|
try {
|
||||||
|
mMulticastSocket.leaveGroup(MULTICAST_IPV6_ADDRESS, mNetworkInterface);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error leaving IPv4 group for " + mNetworkInterface, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mMulticastSocket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the network interface that this socket is bound to. If the interface
|
||||||
|
* cannot be determined, returns -1.
|
||||||
|
*
|
||||||
|
* <p>This method could be used on any thread.
|
||||||
|
*/
|
||||||
|
public int getInterfaceIndex() {
|
||||||
|
return mNetworkInterface.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Returns whether this socket has joined IPv4 group */
|
||||||
|
public boolean hasJoinedIpv4() {
|
||||||
|
return mJoinedIpv4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Returns whether this socket has joined IPv6 group */
|
||||||
|
public boolean hasJoinedIpv6() {
|
||||||
|
return mJoinedIpv6;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
@@ -35,13 +36,16 @@ public class MdnsResponse {
|
|||||||
private MdnsInetAddressRecord inet4AddressRecord;
|
private MdnsInetAddressRecord inet4AddressRecord;
|
||||||
private MdnsInetAddressRecord inet6AddressRecord;
|
private MdnsInetAddressRecord inet6AddressRecord;
|
||||||
private long lastUpdateTime;
|
private long lastUpdateTime;
|
||||||
private int interfaceIndex = MdnsSocket.INTERFACE_INDEX_UNSPECIFIED;
|
private final int interfaceIndex;
|
||||||
|
@Nullable private final Network network;
|
||||||
|
|
||||||
/** Constructs a new, empty response. */
|
/** Constructs a new, empty response. */
|
||||||
public MdnsResponse(long now) {
|
public MdnsResponse(long now, int interfaceIndex, @Nullable Network network) {
|
||||||
lastUpdateTime = now;
|
lastUpdateTime = now;
|
||||||
records = new LinkedList<>();
|
records = new LinkedList<>();
|
||||||
pointerRecords = new LinkedList<>();
|
pointerRecords = new LinkedList<>();
|
||||||
|
this.interfaceIndex = interfaceIndex;
|
||||||
|
this.network = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This generic typed helper compares records for equality.
|
// This generic typed helper compares records for equality.
|
||||||
@@ -207,22 +211,22 @@ public class MdnsResponse {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the index of the network interface at which this response was received. Can be set to
|
|
||||||
* {@link MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if unset.
|
|
||||||
*/
|
|
||||||
public synchronized void setInterfaceIndex(int interfaceIndex) {
|
|
||||||
this.interfaceIndex = interfaceIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the network interface at which this response was received. Can be set to
|
* Returns the index of the network interface at which this response was received. Can be set to
|
||||||
* {@link MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if unset.
|
* {@link MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if unset.
|
||||||
*/
|
*/
|
||||||
public synchronized int getInterfaceIndex() {
|
public int getInterfaceIndex() {
|
||||||
return interfaceIndex;
|
return interfaceIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the network at which this response was received, or null if the network is unknown.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Network getNetwork() {
|
||||||
|
return network;
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets the IPv6 address record. */
|
/** Gets the IPv6 address record. */
|
||||||
public synchronized MdnsInetAddressRecord getInet6AddressRecord() {
|
public synchronized MdnsInetAddressRecord getInet6AddressRecord() {
|
||||||
return inet6AddressRecord;
|
return inet6AddressRecord;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.server.connectivity.mdns;
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.Network;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
||||||
@@ -95,10 +96,11 @@ public class MdnsResponseDecoder {
|
|||||||
* @param packet The packet to read from.
|
* @param packet The packet to read from.
|
||||||
* @param interfaceIndex the network interface index (or {@link
|
* @param interfaceIndex the network interface index (or {@link
|
||||||
* MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if not known) at which the packet was received
|
* MdnsSocket#INTERFACE_INDEX_UNSPECIFIED} if not known) at which the packet was received
|
||||||
|
* @param network the network at which the packet was received, or null if it is unknown.
|
||||||
* @return A list of mDNS responses, or null if the packet contained no appropriate responses.
|
* @return A list of mDNS responses, or null if the packet contained no appropriate responses.
|
||||||
*/
|
*/
|
||||||
public int decode(@NonNull DatagramPacket packet, @NonNull List<MdnsResponse> responses,
|
public int decode(@NonNull DatagramPacket packet, @NonNull List<MdnsResponse> responses,
|
||||||
int interfaceIndex) {
|
int interfaceIndex, @Nullable Network network) {
|
||||||
MdnsPacketReader reader = new MdnsPacketReader(packet);
|
MdnsPacketReader reader = new MdnsPacketReader(packet);
|
||||||
|
|
||||||
List<MdnsRecord> records;
|
List<MdnsRecord> records;
|
||||||
@@ -253,12 +255,11 @@ public class MdnsResponseDecoder {
|
|||||||
MdnsResponse response = findResponseWithPointer(responses,
|
MdnsResponse response = findResponseWithPointer(responses,
|
||||||
pointerRecord.getPointer());
|
pointerRecord.getPointer());
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
response = new MdnsResponse(now);
|
response = new MdnsResponse(now, interfaceIndex, network);
|
||||||
responses.add(response);
|
responses.add(response);
|
||||||
}
|
}
|
||||||
// Set interface index earlier because some responses have PTR record only.
|
// Set interface index earlier because some responses have PTR record only.
|
||||||
// Need to know every response is getting from which interface.
|
// Need to know every response is getting from which interface.
|
||||||
response.setInterfaceIndex(interfaceIndex);
|
|
||||||
response.addPointerRecord((MdnsPointerRecord) record);
|
response.addPointerRecord((MdnsPointerRecord) record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.net.Network;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -43,7 +45,8 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public MdnsSearchOptions createFromParcel(Parcel source) {
|
public MdnsSearchOptions createFromParcel(Parcel source) {
|
||||||
return new MdnsSearchOptions(source.createStringArrayList(),
|
return new MdnsSearchOptions(source.createStringArrayList(),
|
||||||
source.readBoolean(), source.readBoolean());
|
source.readBoolean(), source.readBoolean(),
|
||||||
|
source.readParcelable(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,15 +59,19 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
|
|
||||||
private final boolean isPassiveMode;
|
private final boolean isPassiveMode;
|
||||||
private final boolean removeExpiredService;
|
private final boolean removeExpiredService;
|
||||||
|
// The target network for searching. Null network means search on all possible interfaces.
|
||||||
|
@Nullable private final Network mNetwork;
|
||||||
|
|
||||||
/** Parcelable constructs for a {@link MdnsServiceInfo}. */
|
/** Parcelable constructs for a {@link MdnsSearchOptions}. */
|
||||||
MdnsSearchOptions(List<String> subtypes, boolean isPassiveMode, boolean removeExpiredService) {
|
MdnsSearchOptions(List<String> subtypes, boolean isPassiveMode, boolean removeExpiredService,
|
||||||
|
@Nullable Network network) {
|
||||||
this.subtypes = new ArrayList<>();
|
this.subtypes = new ArrayList<>();
|
||||||
if (subtypes != null) {
|
if (subtypes != null) {
|
||||||
this.subtypes.addAll(subtypes);
|
this.subtypes.addAll(subtypes);
|
||||||
}
|
}
|
||||||
this.isPassiveMode = isPassiveMode;
|
this.isPassiveMode = isPassiveMode;
|
||||||
this.removeExpiredService = removeExpiredService;
|
this.removeExpiredService = removeExpiredService;
|
||||||
|
mNetwork = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a {@link Builder} for {@link MdnsSearchOptions}. */
|
/** Returns a {@link Builder} for {@link MdnsSearchOptions}. */
|
||||||
@@ -98,6 +105,16 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
return removeExpiredService;
|
return removeExpiredService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the network which the mdns query should target on.
|
||||||
|
*
|
||||||
|
* @return the target network or null if search on all possible interfaces.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Network getNetwork() {
|
||||||
|
return mNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -108,6 +125,7 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
out.writeStringList(subtypes);
|
out.writeStringList(subtypes);
|
||||||
out.writeBoolean(isPassiveMode);
|
out.writeBoolean(isPassiveMode);
|
||||||
out.writeBoolean(removeExpiredService);
|
out.writeBoolean(removeExpiredService);
|
||||||
|
out.writeParcelable(mNetwork, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A builder to create {@link MdnsSearchOptions}. */
|
/** A builder to create {@link MdnsSearchOptions}. */
|
||||||
@@ -115,6 +133,7 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
private final Set<String> subtypes;
|
private final Set<String> subtypes;
|
||||||
private boolean isPassiveMode = true;
|
private boolean isPassiveMode = true;
|
||||||
private boolean removeExpiredService;
|
private boolean removeExpiredService;
|
||||||
|
private Network mNetwork;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
subtypes = new ArraySet<>();
|
subtypes = new ArraySet<>();
|
||||||
@@ -165,10 +184,20 @@ public class MdnsSearchOptions implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if the mdns query should target on specific network.
|
||||||
|
*
|
||||||
|
* @param network the mdns query will target on given network.
|
||||||
|
*/
|
||||||
|
public Builder setNetwork(Network network) {
|
||||||
|
mNetwork = network;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */
|
/** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */
|
||||||
public MdnsSearchOptions build() {
|
public MdnsSearchOptions build() {
|
||||||
return new MdnsSearchOptions(
|
return new MdnsSearchOptions(new ArrayList<>(subtypes), isPassiveMode,
|
||||||
new ArrayList<>(subtypes), isPassiveMode, removeExpiredService);
|
removeExpiredService, mNetwork);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSocket.INTERFACE_INDEX_UNSPECIFIED;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.Network;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -58,7 +61,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
source.readString(),
|
source.readString(),
|
||||||
source.createStringArrayList(),
|
source.createStringArrayList(),
|
||||||
source.createTypedArrayList(TextEntry.CREATOR),
|
source.createTypedArrayList(TextEntry.CREATOR),
|
||||||
source.readInt());
|
source.readInt(),
|
||||||
|
source.readParcelable(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,6 +86,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
private final int interfaceIndex;
|
private final int interfaceIndex;
|
||||||
|
|
||||||
private final Map<String, byte[]> attributes;
|
private final Map<String, byte[]> attributes;
|
||||||
|
@Nullable
|
||||||
|
private final Network network;
|
||||||
|
|
||||||
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
||||||
public MdnsServiceInfo(
|
public MdnsServiceInfo(
|
||||||
@@ -103,7 +109,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
ipv6Address,
|
ipv6Address,
|
||||||
textStrings,
|
textStrings,
|
||||||
/* textEntries= */ null,
|
/* textEntries= */ null,
|
||||||
/* interfaceIndex= */ -1);
|
/* interfaceIndex= */ INTERFACE_INDEX_UNSPECIFIED,
|
||||||
|
/* network= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
/** Constructs a {@link MdnsServiceInfo} object with default values. */
|
||||||
@@ -127,7 +134,8 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
ipv6Address,
|
ipv6Address,
|
||||||
textStrings,
|
textStrings,
|
||||||
textEntries,
|
textEntries,
|
||||||
/* interfaceIndex= */ -1);
|
/* interfaceIndex= */ INTERFACE_INDEX_UNSPECIFIED,
|
||||||
|
/* network= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,6 +154,37 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
@Nullable List<String> textStrings,
|
@Nullable List<String> textStrings,
|
||||||
@Nullable List<TextEntry> textEntries,
|
@Nullable List<TextEntry> textEntries,
|
||||||
int interfaceIndex) {
|
int interfaceIndex) {
|
||||||
|
this(
|
||||||
|
serviceInstanceName,
|
||||||
|
serviceType,
|
||||||
|
subtypes,
|
||||||
|
hostName,
|
||||||
|
port,
|
||||||
|
ipv4Address,
|
||||||
|
ipv6Address,
|
||||||
|
textStrings,
|
||||||
|
textEntries,
|
||||||
|
interfaceIndex,
|
||||||
|
/* network= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link MdnsServiceInfo} object with default values.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public MdnsServiceInfo(
|
||||||
|
String serviceInstanceName,
|
||||||
|
String[] serviceType,
|
||||||
|
@Nullable List<String> subtypes,
|
||||||
|
String[] hostName,
|
||||||
|
int port,
|
||||||
|
@Nullable String ipv4Address,
|
||||||
|
@Nullable String ipv6Address,
|
||||||
|
@Nullable List<String> textStrings,
|
||||||
|
@Nullable List<TextEntry> textEntries,
|
||||||
|
int interfaceIndex,
|
||||||
|
@Nullable Network network) {
|
||||||
this.serviceInstanceName = serviceInstanceName;
|
this.serviceInstanceName = serviceInstanceName;
|
||||||
this.serviceType = serviceType;
|
this.serviceType = serviceType;
|
||||||
this.subtypes = new ArrayList<>();
|
this.subtypes = new ArrayList<>();
|
||||||
@@ -180,6 +219,7 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
this.attributes = Collections.unmodifiableMap(attributes);
|
this.attributes = Collections.unmodifiableMap(attributes);
|
||||||
this.interfaceIndex = interfaceIndex;
|
this.interfaceIndex = interfaceIndex;
|
||||||
|
this.network = network;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<TextEntry> parseTextStrings(List<String> textStrings) {
|
private static List<TextEntry> parseTextStrings(List<String> textStrings) {
|
||||||
@@ -243,6 +283,14 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
return interfaceIndex;
|
return interfaceIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the network at which this response was received, or null if the network is unknown.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Network getNetwork() {
|
||||||
|
return network;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns attribute value for {@code key} as a UTF-8 string. It's the caller who must make sure
|
* Returns attribute value for {@code key} as a UTF-8 string. It's the caller who must make sure
|
||||||
* that the value of {@code key} is indeed a UTF-8 string. {@code null} will be returned if no
|
* that the value of {@code key} is indeed a UTF-8 string. {@code null} will be returned if no
|
||||||
@@ -293,6 +341,7 @@ public class MdnsServiceInfo implements Parcelable {
|
|||||||
out.writeStringList(textStrings);
|
out.writeStringList(textStrings);
|
||||||
out.writeTypedList(textEntries);
|
out.writeTypedList(textEntries);
|
||||||
out.writeInt(interfaceIndex);
|
out.writeInt(interfaceIndex);
|
||||||
|
out.writeParcelable(network, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ public class MdnsServiceTypeClient {
|
|||||||
ipv6Address,
|
ipv6Address,
|
||||||
textStrings,
|
textStrings,
|
||||||
textEntries,
|
textEntries,
|
||||||
response.getInterfaceIndex());
|
response.getInterfaceIndex(),
|
||||||
|
response.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
||||||
@@ -38,9 +40,9 @@ public class MdnsSocket {
|
|||||||
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsSocket");
|
private static final MdnsLogger LOGGER = new MdnsLogger("MdnsSocket");
|
||||||
|
|
||||||
static final int INTERFACE_INDEX_UNSPECIFIED = -1;
|
static final int INTERFACE_INDEX_UNSPECIFIED = -1;
|
||||||
private static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
|
protected static final InetSocketAddress MULTICAST_IPV4_ADDRESS =
|
||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
|
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT);
|
||||||
private static final InetSocketAddress MULTICAST_IPV6_ADDRESS =
|
protected static final InetSocketAddress MULTICAST_IPV6_ADDRESS =
|
||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
|
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT);
|
||||||
private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider;
|
private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider;
|
||||||
private final MulticastSocket multicastSocket;
|
private final MulticastSocket multicastSocket;
|
||||||
@@ -125,6 +127,14 @@ public class MdnsSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the available network that this socket is used to, or null if the network is unknown.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Network getNetwork() {
|
||||||
|
return multicastNetworkInterfaceProvider.getAvailableNetwork();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOnIPv6OnlyNetwork() {
|
public boolean isOnIPv6OnlyNetwork() {
|
||||||
return isOnIPv6OnlyNetwork;
|
return isOnIPv6OnlyNetwork;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.Network;
|
||||||
import android.net.wifi.WifiManager.MulticastLock;
|
import android.net.wifi.WifiManager.MulticastLock;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
@@ -397,7 +398,8 @@ public class MdnsSocketClient {
|
|||||||
responseType,
|
responseType,
|
||||||
/* interfaceIndex= */ (socket == null || !propagateInterfaceIndex)
|
/* interfaceIndex= */ (socket == null || !propagateInterfaceIndex)
|
||||||
? MdnsSocket.INTERFACE_INDEX_UNSPECIFIED
|
? MdnsSocket.INTERFACE_INDEX_UNSPECIFIED
|
||||||
: socket.getInterfaceIndex());
|
: socket.getInterfaceIndex(),
|
||||||
|
/* network= */ socket.getNetwork());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (!shouldStopSocketLoop) {
|
if (!shouldStopSocketLoop) {
|
||||||
@@ -408,12 +410,12 @@ public class MdnsSocketClient {
|
|||||||
LOGGER.log("Receive thread stopped.");
|
LOGGER.log("Receive thread stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int processResponsePacket(
|
private int processResponsePacket(@NonNull DatagramPacket packet, String responseType,
|
||||||
@NonNull DatagramPacket packet, String responseType, int interfaceIndex) {
|
int interfaceIndex, @Nullable Network network) {
|
||||||
int packetNumber = ++receivedPacketNumber;
|
int packetNumber = ++receivedPacketNumber;
|
||||||
|
|
||||||
List<MdnsResponse> responses = new LinkedList<>();
|
List<MdnsResponse> responses = new LinkedList<>();
|
||||||
int errorCode = responseDecoder.decode(packet, responses, interfaceIndex);
|
int errorCode = responseDecoder.decode(packet, responses, interfaceIndex, network);
|
||||||
if (errorCode == MdnsResponseDecoder.SUCCESS) {
|
if (errorCode == MdnsResponseDecoder.SUCCESS) {
|
||||||
if (responseType.equals(MULTICAST_TYPE)) {
|
if (responseType.equals(MULTICAST_TYPE)) {
|
||||||
receivedMulticastResponse = true;
|
receivedMulticastResponse = true;
|
||||||
|
|||||||
@@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
|
import android.net.INetd;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.NetworkRequest;
|
||||||
|
import android.net.TetheringManager;
|
||||||
|
import android.net.TetheringManager.TetheringEventCallback;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.system.OsConstants;
|
||||||
|
import android.util.ArrayMap;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
|
||||||
|
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.server.connectivity.mdns.util.MdnsLogger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InterfaceAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link MdnsSocketProvider} manages the multiple sockets for mDns.
|
||||||
|
*
|
||||||
|
* <p>This class is not thread safe, it is intended to be used only from the looper thread.
|
||||||
|
* However, the constructor is an exception, as it is called on another thread;
|
||||||
|
* therefore for thread safety all members of this class MUST either be final or initialized
|
||||||
|
* to their default value (0, false or null).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MdnsSocketProvider {
|
||||||
|
private static final String TAG = MdnsSocketProvider.class.getSimpleName();
|
||||||
|
private static final boolean DBG = MdnsDiscoveryManager.DBG;
|
||||||
|
private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
|
||||||
|
@NonNull private final Context mContext;
|
||||||
|
@NonNull private final Handler mHandler;
|
||||||
|
@NonNull private final Dependencies mDependencies;
|
||||||
|
@NonNull private final NetworkCallback mNetworkCallback;
|
||||||
|
@NonNull private final TetheringEventCallback mTetheringEventCallback;
|
||||||
|
@NonNull private final NetlinkMonitor mNetlinkMonitor;
|
||||||
|
private final ArrayMap<Network, SocketInfo> mNetworkSockets = new ArrayMap<>();
|
||||||
|
private final ArrayMap<String, SocketInfo> mTetherInterfaceSockets = new ArrayMap<>();
|
||||||
|
private final ArrayMap<Network, LinkProperties> mActiveNetworksLinkProperties =
|
||||||
|
new ArrayMap<>();
|
||||||
|
private final ArrayMap<SocketCallback, Network> mCallbacksToRequestedNetworks =
|
||||||
|
new ArrayMap<>();
|
||||||
|
private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
|
||||||
|
private final List<String> mTetheredInterfaces = new ArrayList<>();
|
||||||
|
private boolean mMonitoringSockets = false;
|
||||||
|
|
||||||
|
public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper) {
|
||||||
|
this(context, looper, new Dependencies());
|
||||||
|
}
|
||||||
|
|
||||||
|
MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
|
||||||
|
@NonNull Dependencies deps) {
|
||||||
|
mContext = context;
|
||||||
|
mHandler = new Handler(looper);
|
||||||
|
mDependencies = deps;
|
||||||
|
mNetworkCallback = new NetworkCallback() {
|
||||||
|
@Override
|
||||||
|
public void onLost(Network network) {
|
||||||
|
mActiveNetworksLinkProperties.remove(network);
|
||||||
|
removeSocket(network, null /* interfaceName */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
|
||||||
|
handleLinkPropertiesChanged(network, lp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mTetheringEventCallback = new TetheringEventCallback() {
|
||||||
|
@Override
|
||||||
|
public void onLocalOnlyInterfacesChanged(@NonNull List<String> interfaces) {
|
||||||
|
handleTetherInterfacesChanged(mLocalOnlyInterfaces, interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {
|
||||||
|
handleTetherInterfacesChanged(mTetheredInterfaces, interfaces);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mNetlinkMonitor = new SocketNetlinkMonitor(mHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependencies of MdnsSocketProvider, for injection in tests.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public static class Dependencies {
|
||||||
|
/*** Get network interface by given interface name */
|
||||||
|
public NetworkInterfaceWrapper getNetworkInterfaceByName(String interfaceName)
|
||||||
|
throws SocketException {
|
||||||
|
final NetworkInterface ni = NetworkInterface.getByName(interfaceName);
|
||||||
|
return ni == null ? null : new NetworkInterfaceWrapper(ni);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Check whether given network interface can support mdns */
|
||||||
|
public boolean canScanOnInterface(NetworkInterfaceWrapper networkInterface) {
|
||||||
|
return MulticastNetworkInterfaceProvider.canScanOnInterface(networkInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Create a MdnsInterfaceSocket */
|
||||||
|
public MdnsInterfaceSocket createMdnsInterfaceSocket(NetworkInterface networkInterface,
|
||||||
|
int port) throws IOException {
|
||||||
|
return new MdnsInterfaceSocket(networkInterface, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Data class for storing socket related info */
|
||||||
|
private static class SocketInfo {
|
||||||
|
final MdnsInterfaceSocket mSocket;
|
||||||
|
final List<LinkAddress> mAddresses = new ArrayList<>();
|
||||||
|
|
||||||
|
SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses) {
|
||||||
|
mSocket = socket;
|
||||||
|
mAddresses.addAll(addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SocketNetlinkMonitor extends NetlinkMonitor {
|
||||||
|
SocketNetlinkMonitor(Handler handler) {
|
||||||
|
super(handler, LOGGER.mLog, TAG, OsConstants.NETLINK_ROUTE,
|
||||||
|
NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
|
||||||
|
// TODO: Handle netlink message.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureRunningOnHandlerThread() {
|
||||||
|
if (mHandler.getLooper().getThread() != Thread.currentThread()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Not running on Handler thread: " + Thread.currentThread().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Start monitoring sockets by listening callbacks for sockets creation or removal */
|
||||||
|
public void startMonitoringSockets() {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
if (mMonitoringSockets) {
|
||||||
|
Log.d(TAG, "Already monitoring sockets.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DBG) Log.d(TAG, "Start monitoring sockets.");
|
||||||
|
mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
|
||||||
|
new NetworkRequest.Builder().clearCapabilities().build(),
|
||||||
|
mNetworkCallback, mHandler);
|
||||||
|
|
||||||
|
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
|
||||||
|
tetheringManager.registerTetheringEventCallback(mHandler::post, mTetheringEventCallback);
|
||||||
|
|
||||||
|
mHandler.post(mNetlinkMonitor::start);
|
||||||
|
mMonitoringSockets = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Stop monitoring sockets and unregister callbacks */
|
||||||
|
public void stopMonitoringSockets() {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
if (!mMonitoringSockets) {
|
||||||
|
Log.d(TAG, "Monitoring sockets hasn't been started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DBG) Log.d(TAG, "Stop monitoring sockets.");
|
||||||
|
mContext.getSystemService(ConnectivityManager.class)
|
||||||
|
.unregisterNetworkCallback(mNetworkCallback);
|
||||||
|
|
||||||
|
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
|
||||||
|
tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
|
||||||
|
|
||||||
|
mHandler.post(mNetlinkMonitor::stop);
|
||||||
|
mMonitoringSockets = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNetworkMatched(@Nullable Network targetNetwork,
|
||||||
|
@NonNull Network currentNetwork) {
|
||||||
|
return targetNetwork == null || targetNetwork.equals(currentNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchRequestedNetwork(Network network) {
|
||||||
|
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
|
||||||
|
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
|
||||||
|
if (isNetworkMatched(requestedNetwork, network)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAllNetworksRequest() {
|
||||||
|
return mCallbacksToRequestedNetworks.containsValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLinkPropertiesChanged(Network network, LinkProperties lp) {
|
||||||
|
mActiveNetworksLinkProperties.put(network, lp);
|
||||||
|
if (!matchRequestedNetwork(network)) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Ignore LinkProperties change. There is no request for the"
|
||||||
|
+ " Network:" + network);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SocketInfo socketInfo = mNetworkSockets.get(network);
|
||||||
|
if (socketInfo == null) {
|
||||||
|
createSocket(network, 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, lp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkProperties createLPForTetheredInterface(String interfaceName) {
|
||||||
|
final LinkProperties linkProperties = new LinkProperties();
|
||||||
|
linkProperties.setInterfaceName(interfaceName);
|
||||||
|
// TODO: Use NetlinkMonitor to update addresses for tethering interfaces.
|
||||||
|
return linkProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTetherInterfacesChanged(List<String> current, List<String> updated) {
|
||||||
|
if (!hasAllNetworksRequest()) {
|
||||||
|
// Currently, the network for tethering can not be requested, so the sockets for
|
||||||
|
// tethering are only created if there is a request for all networks (interfaces).
|
||||||
|
// Therefore, this change can skip if there is no such request.
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Ignore tether interfaces change. There is no request for all"
|
||||||
|
+ " networks.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CompareResult<String> interfaceDiff = new CompareResult<>(
|
||||||
|
current, updated);
|
||||||
|
for (String name : interfaceDiff.added) {
|
||||||
|
createSocket(new Network(INetd.LOCAL_NET_ID), createLPForTetheredInterface(name));
|
||||||
|
}
|
||||||
|
for (String name : interfaceDiff.removed) {
|
||||||
|
removeSocket(new Network(INetd.LOCAL_NET_ID), name);
|
||||||
|
}
|
||||||
|
current.clear();
|
||||||
|
current.addAll(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<LinkAddress> getLinkAddressFromNetworkInterface(
|
||||||
|
NetworkInterfaceWrapper networkInterface) {
|
||||||
|
List<LinkAddress> addresses = new ArrayList<>();
|
||||||
|
for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
|
||||||
|
addresses.add(new LinkAddress(address));
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSocket(Network network, LinkProperties lp) {
|
||||||
|
final String interfaceName = lp.getInterfaceName();
|
||||||
|
if (interfaceName == null) {
|
||||||
|
Log.e(TAG, "Can not create socket with null interface name.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final NetworkInterfaceWrapper networkInterface =
|
||||||
|
mDependencies.getNetworkInterfaceByName(interfaceName);
|
||||||
|
if (networkInterface == null || !mDependencies.canScanOnInterface(networkInterface)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Create a socket on network:" + network
|
||||||
|
+ " with interfaceName:" + interfaceName);
|
||||||
|
}
|
||||||
|
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
|
||||||
|
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT);
|
||||||
|
final List<LinkAddress> addresses;
|
||||||
|
if (network.netId == INetd.LOCAL_NET_ID) {
|
||||||
|
addresses = getLinkAddressFromNetworkInterface(networkInterface);
|
||||||
|
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
|
||||||
|
} else {
|
||||||
|
addresses = lp.getLinkAddresses();
|
||||||
|
mNetworkSockets.put(network, new SocketInfo(socket, addresses));
|
||||||
|
}
|
||||||
|
// Try to join IPv4/IPv6 group.
|
||||||
|
socket.joinGroup(addresses);
|
||||||
|
|
||||||
|
// Notify the listeners which need this socket.
|
||||||
|
notifySocketCreated(network, socket, addresses);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Create a socket failed with interface=" + interfaceName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSocket(Network network, String interfaceName) {
|
||||||
|
final SocketInfo socketInfo = network.netId == INetd.LOCAL_NET_ID
|
||||||
|
? mTetherInterfaceSockets.remove(interfaceName)
|
||||||
|
: mNetworkSockets.remove(network);
|
||||||
|
if (socketInfo == null) return;
|
||||||
|
|
||||||
|
socketInfo.mSocket.destroy();
|
||||||
|
notifyInterfaceDestroyed(network, socketInfo.mSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifySocketCreated(Network network, MdnsInterfaceSocket socket,
|
||||||
|
List<LinkAddress> addresses) {
|
||||||
|
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
|
||||||
|
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
|
||||||
|
if (isNetworkMatched(requestedNetwork, network)) {
|
||||||
|
mCallbacksToRequestedNetworks.keyAt(i).onSocketCreated(network, socket, addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyInterfaceDestroyed(Network network, MdnsInterfaceSocket socket) {
|
||||||
|
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
|
||||||
|
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
|
||||||
|
if (isNetworkMatched(requestedNetwork, network)) {
|
||||||
|
mCallbacksToRequestedNetworks.keyAt(i).onInterfaceDestroyed(network, socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyAddressesChanged(Network network, LinkProperties lp) {
|
||||||
|
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
|
||||||
|
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
|
||||||
|
if (isNetworkMatched(requestedNetwork, network)) {
|
||||||
|
mCallbacksToRequestedNetworks.keyAt(i)
|
||||||
|
.onAddressesChanged(network, lp.getLinkAddresses());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retrieveAndNotifySocketFromNetwork(Network network, SocketCallback cb) {
|
||||||
|
final SocketInfo socketInfo = mNetworkSockets.get(network);
|
||||||
|
if (socketInfo == null) {
|
||||||
|
final LinkProperties lp = mActiveNetworksLinkProperties.get(network);
|
||||||
|
if (lp == null) {
|
||||||
|
// The requested network is not existed. Maybe wait for LinkProperties change later.
|
||||||
|
if (DBG) Log.d(TAG, "There is no LinkProperties for this network:" + network);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createSocket(network, lp);
|
||||||
|
} else {
|
||||||
|
// Notify the socket for requested network.
|
||||||
|
cb.onSocketCreated(network, socketInfo.mSocket, socketInfo.mAddresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
|
||||||
|
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
|
||||||
|
if (socketInfo == null) {
|
||||||
|
createSocket(
|
||||||
|
new Network(INetd.LOCAL_NET_ID), createLPForTetheredInterface(interfaceName));
|
||||||
|
} else {
|
||||||
|
// Notify the socket for requested network.
|
||||||
|
cb.onSocketCreated(
|
||||||
|
new Network(INetd.LOCAL_NET_ID), socketInfo.mSocket, socketInfo.mAddresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a socket for given network.
|
||||||
|
*
|
||||||
|
* @param network the required network for a socket. Null means create sockets on all possible
|
||||||
|
* networks (interfaces).
|
||||||
|
* @param cb the callback to listen the socket creation.
|
||||||
|
*/
|
||||||
|
public void requestSocket(@Nullable Network network, @NonNull SocketCallback cb) {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
mCallbacksToRequestedNetworks.put(cb, network);
|
||||||
|
if (network == null) {
|
||||||
|
// Does not specify a required network, create sockets for all possible
|
||||||
|
// networks (interfaces).
|
||||||
|
for (int i = 0; i < mActiveNetworksLinkProperties.size(); i++) {
|
||||||
|
retrieveAndNotifySocketFromNetwork(mActiveNetworksLinkProperties.keyAt(i), cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String localInterface : mLocalOnlyInterfaces) {
|
||||||
|
retrieveAndNotifySocketFromInterface(localInterface, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String tetheredInterface : mTetheredInterfaces) {
|
||||||
|
retrieveAndNotifySocketFromInterface(tetheredInterface, cb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retrieveAndNotifySocketFromNetwork(network, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Unrequest the socket */
|
||||||
|
public void unrequestSocket(@NonNull SocketCallback cb) {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
mCallbacksToRequestedNetworks.remove(cb);
|
||||||
|
if (hasAllNetworksRequest()) {
|
||||||
|
// Still has a request for all networks (interfaces).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if remaining requests are matched any of sockets.
|
||||||
|
for (int i = mNetworkSockets.size() - 1; i >= 0; i--) {
|
||||||
|
if (matchRequestedNetwork(mNetworkSockets.keyAt(i))) continue;
|
||||||
|
mNetworkSockets.removeAt(i).mSocket.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all sockets for tethering interface because these sockets do not have associated
|
||||||
|
// networks, and they should invoke by a request for all networks (interfaces). If there is
|
||||||
|
// no such request, the sockets for tethering interface should be removed.
|
||||||
|
for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
|
||||||
|
mTetherInterfaceSockets.removeAt(i).mSocket.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Callbacks for listening socket changes */
|
||||||
|
public interface SocketCallback {
|
||||||
|
/*** Notify the socket is created */
|
||||||
|
default void onSocketCreated(@NonNull Network network, @NonNull MdnsInterfaceSocket socket,
|
||||||
|
@NonNull List<LinkAddress> addresses) {}
|
||||||
|
/*** Notify the interface is destroyed */
|
||||||
|
default void onInterfaceDestroyed(@NonNull Network network,
|
||||||
|
@NonNull MdnsInterfaceSocket socket) {}
|
||||||
|
/*** Notify the addresses is changed on the network */
|
||||||
|
default void onAddressesChanged(@NonNull Network network,
|
||||||
|
@NonNull List<LinkAddress> addresses) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity.mdns;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
import com.android.server.connectivity.mdns.util.MdnsLogger;
|
||||||
@@ -56,7 +57,7 @@ public class MulticastNetworkInterfaceProvider {
|
|||||||
context, this::onConnectivityChanged);
|
context, this::onConnectivityChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onConnectivityChanged() {
|
private synchronized void onConnectivityChanged() {
|
||||||
connectivityChanged = true;
|
connectivityChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +142,13 @@ public class MulticastNetworkInterfaceProvider {
|
|||||||
return networkInterfaceWrappers;
|
return networkInterfaceWrappers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
|
@Nullable
|
||||||
|
public Network getAvailableNetwork() {
|
||||||
|
return connectivityMonitor.getAvailableNetwork();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Check whether given network interface can support mdns */
|
||||||
|
public static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
|
||||||
try {
|
try {
|
||||||
if ((networkInterface == null)
|
if ((networkInterface == null)
|
||||||
|| networkInterface.isLoopback()
|
|| networkInterface.isLoopback()
|
||||||
@@ -160,7 +167,7 @@ public class MulticastNetworkInterfaceProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasInet4Address(@NonNull NetworkInterfaceWrapper networkInterface) {
|
private static boolean hasInet4Address(@NonNull NetworkInterfaceWrapper networkInterface) {
|
||||||
for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
|
for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
|
||||||
if (ifAddr.getAddress() instanceof Inet4Address) {
|
if (ifAddr.getAddress() instanceof Inet4Address) {
|
||||||
return true;
|
return true;
|
||||||
@@ -169,7 +176,7 @@ public class MulticastNetworkInterfaceProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasInet6Address(@NonNull NetworkInterfaceWrapper networkInterface) {
|
private static boolean hasInet6Address(@NonNull NetworkInterfaceWrapper networkInterface) {
|
||||||
for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
|
for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
|
||||||
if (ifAddr.getAddress() instanceof Inet6Address) {
|
if (ifAddr.getAddress() instanceof Inet6Address) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ public class ConnectivityMonitorWithConnectivityManagerTests {
|
|||||||
any(NetworkRequest.class), callbackCaptor.capture());
|
any(NetworkRequest.class), callbackCaptor.capture());
|
||||||
|
|
||||||
final NetworkCallback callback = callbackCaptor.getValue();
|
final NetworkCallback callback = callbackCaptor.getValue();
|
||||||
final Network testNetwork = new Network(1 /* netId */);
|
final Network testNetwork = mock(Network.class);
|
||||||
|
|
||||||
// Simulate network available.
|
// Simulate network available.
|
||||||
callback.onAvailable(testNetwork);
|
callback.onAvailable(testNetwork);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import android.net.InetAddresses;
|
import android.net.InetAddresses;
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
import com.android.net.module.util.HexDump;
|
import com.android.net.module.util.HexDump;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
@@ -165,7 +166,8 @@ public class MdnsResponseDecoderTests {
|
|||||||
packet.setSocketAddress(
|
packet.setSocketAddress(
|
||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED, mock(Network.class));
|
||||||
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
||||||
assertEquals(1, responses.size());
|
assertEquals(1, responses.size());
|
||||||
}
|
}
|
||||||
@@ -178,7 +180,8 @@ public class MdnsResponseDecoderTests {
|
|||||||
packet.setSocketAddress(
|
packet.setSocketAddress(
|
||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT));
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED, mock(Network.class));
|
||||||
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
||||||
assertEquals(2, responses.size());
|
assertEquals(2, responses.size());
|
||||||
}
|
}
|
||||||
@@ -237,7 +240,8 @@ public class MdnsResponseDecoderTests {
|
|||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
||||||
|
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED);
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, MdnsSocket.INTERFACE_INDEX_UNSPECIFIED, mock(Network.class));
|
||||||
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
||||||
|
|
||||||
MdnsResponse response = responses.get(0);
|
MdnsResponse response = responses.get(0);
|
||||||
@@ -287,10 +291,13 @@ public class MdnsResponseDecoderTests {
|
|||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
||||||
|
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 10);
|
final Network network = mock(Network.class);
|
||||||
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, /* interfaceIndex= */ 10, network);
|
||||||
assertEquals(errorCode, MdnsResponseDecoder.SUCCESS);
|
assertEquals(errorCode, MdnsResponseDecoder.SUCCESS);
|
||||||
assertEquals(responses.size(), 1);
|
assertEquals(responses.size(), 1);
|
||||||
assertEquals(responses.get(0).getInterfaceIndex(), 10);
|
assertEquals(responses.get(0).getInterfaceIndex(), 10);
|
||||||
|
assertEquals(network, responses.get(0).getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -306,7 +313,8 @@ public class MdnsResponseDecoderTests {
|
|||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
||||||
|
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, /* interfaceIndex= */ 0, mock(Network.class));
|
||||||
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
||||||
|
|
||||||
// This should emit two records:
|
// This should emit two records:
|
||||||
@@ -340,7 +348,8 @@ public class MdnsResponseDecoderTests {
|
|||||||
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT));
|
||||||
|
|
||||||
responses.clear();
|
responses.clear();
|
||||||
int errorCode = decoder.decode(packet, responses, /* interfaceIndex= */ 0);
|
int errorCode = decoder.decode(
|
||||||
|
packet, responses, /* interfaceIndex= */ 0, mock(Network.class));
|
||||||
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
assertEquals(MdnsResponseDecoder.SUCCESS, errorCode);
|
||||||
|
|
||||||
// This should emit only two records:
|
// This should emit only two records:
|
||||||
|
|||||||
@@ -21,8 +21,12 @@ import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.net.Network;
|
||||||
|
|
||||||
import com.android.net.module.util.HexDump;
|
import com.android.net.module.util.HexDump;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
@@ -92,6 +96,9 @@ public class MdnsResponseTests {
|
|||||||
+ "3839300878797A3D"
|
+ "3839300878797A3D"
|
||||||
+ "21402324");
|
+ "21402324");
|
||||||
|
|
||||||
|
private static final int INTERFACE_INDEX = 999;
|
||||||
|
private final Network mNetwork = mock(Network.class);
|
||||||
|
|
||||||
// The following helper classes act as wrappers so that IPv4 and IPv6 address records can
|
// The following helper classes act as wrappers so that IPv4 and IPv6 address records can
|
||||||
// be explicitly created by type using same constructor signature as all other records.
|
// be explicitly created by type using same constructor signature as all other records.
|
||||||
static class MdnsInet4AddressRecord extends MdnsInetAddressRecord {
|
static class MdnsInet4AddressRecord extends MdnsInetAddressRecord {
|
||||||
@@ -127,7 +134,7 @@ public class MdnsResponseTests {
|
|||||||
// Construct an MdnsResponse with the specified data packets applied.
|
// Construct an MdnsResponse with the specified data packets applied.
|
||||||
private MdnsResponse makeMdnsResponse(long time, List<PacketAndRecordClass> responseList)
|
private MdnsResponse makeMdnsResponse(long time, List<PacketAndRecordClass> responseList)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
MdnsResponse response = new MdnsResponse(time);
|
MdnsResponse response = new MdnsResponse(time, INTERFACE_INDEX, mNetwork);
|
||||||
for (PacketAndRecordClass responseData : responseList) {
|
for (PacketAndRecordClass responseData : responseList) {
|
||||||
DatagramPacket packet =
|
DatagramPacket packet =
|
||||||
new DatagramPacket(responseData.packetData, responseData.packetData.length);
|
new DatagramPacket(responseData.packetData, responseData.packetData.length);
|
||||||
@@ -159,7 +166,7 @@ public class MdnsResponseTests {
|
|||||||
String[] name = reader.readLabels();
|
String[] name = reader.readLabels();
|
||||||
reader.skip(2); // skip record type indication.
|
reader.skip(2); // skip record type indication.
|
||||||
MdnsInetAddressRecord record = new MdnsInetAddressRecord(name, MdnsRecord.TYPE_A, reader);
|
MdnsInetAddressRecord record = new MdnsInetAddressRecord(name, MdnsRecord.TYPE_A, reader);
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, INTERFACE_INDEX, mNetwork);
|
||||||
assertFalse(response.hasInet4AddressRecord());
|
assertFalse(response.hasInet4AddressRecord());
|
||||||
assertTrue(response.setInet4AddressRecord(record));
|
assertTrue(response.setInet4AddressRecord(record));
|
||||||
assertEquals(response.getInet4AddressRecord(), record);
|
assertEquals(response.getInet4AddressRecord(), record);
|
||||||
@@ -173,7 +180,7 @@ public class MdnsResponseTests {
|
|||||||
reader.skip(2); // skip record type indication.
|
reader.skip(2); // skip record type indication.
|
||||||
MdnsInetAddressRecord record =
|
MdnsInetAddressRecord record =
|
||||||
new MdnsInetAddressRecord(name, MdnsRecord.TYPE_AAAA, reader);
|
new MdnsInetAddressRecord(name, MdnsRecord.TYPE_AAAA, reader);
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, INTERFACE_INDEX, mNetwork);
|
||||||
assertFalse(response.hasInet6AddressRecord());
|
assertFalse(response.hasInet6AddressRecord());
|
||||||
assertTrue(response.setInet6AddressRecord(record));
|
assertTrue(response.setInet6AddressRecord(record));
|
||||||
assertEquals(response.getInet6AddressRecord(), record);
|
assertEquals(response.getInet6AddressRecord(), record);
|
||||||
@@ -186,7 +193,7 @@ public class MdnsResponseTests {
|
|||||||
String[] name = reader.readLabels();
|
String[] name = reader.readLabels();
|
||||||
reader.skip(2); // skip record type indication.
|
reader.skip(2); // skip record type indication.
|
||||||
MdnsPointerRecord record = new MdnsPointerRecord(name, reader);
|
MdnsPointerRecord record = new MdnsPointerRecord(name, reader);
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, INTERFACE_INDEX, mNetwork);
|
||||||
assertFalse(response.hasPointerRecords());
|
assertFalse(response.hasPointerRecords());
|
||||||
assertTrue(response.addPointerRecord(record));
|
assertTrue(response.addPointerRecord(record));
|
||||||
List<MdnsPointerRecord> recordList = response.getPointerRecords();
|
List<MdnsPointerRecord> recordList = response.getPointerRecords();
|
||||||
@@ -202,7 +209,7 @@ public class MdnsResponseTests {
|
|||||||
String[] name = reader.readLabels();
|
String[] name = reader.readLabels();
|
||||||
reader.skip(2); // skip record type indication.
|
reader.skip(2); // skip record type indication.
|
||||||
MdnsServiceRecord record = new MdnsServiceRecord(name, reader);
|
MdnsServiceRecord record = new MdnsServiceRecord(name, reader);
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, INTERFACE_INDEX, mNetwork);
|
||||||
assertFalse(response.hasServiceRecord());
|
assertFalse(response.hasServiceRecord());
|
||||||
assertTrue(response.setServiceRecord(record));
|
assertTrue(response.setServiceRecord(record));
|
||||||
assertEquals(response.getServiceRecord(), record);
|
assertEquals(response.getServiceRecord(), record);
|
||||||
@@ -215,23 +222,31 @@ public class MdnsResponseTests {
|
|||||||
String[] name = reader.readLabels();
|
String[] name = reader.readLabels();
|
||||||
reader.skip(2); // skip record type indication.
|
reader.skip(2); // skip record type indication.
|
||||||
MdnsTextRecord record = new MdnsTextRecord(name, reader);
|
MdnsTextRecord record = new MdnsTextRecord(name, reader);
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, INTERFACE_INDEX, mNetwork);
|
||||||
assertFalse(response.hasTextRecord());
|
assertFalse(response.hasTextRecord());
|
||||||
assertTrue(response.setTextRecord(record));
|
assertTrue(response.setTextRecord(record));
|
||||||
assertEquals(response.getTextRecord(), record);
|
assertEquals(response.getTextRecord(), record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getInterfaceIndex_returnsDefaultValue() {
|
public void getInterfaceIndex() {
|
||||||
MdnsResponse response = new MdnsResponse(/* now= */ 0);
|
final MdnsResponse response1 = new MdnsResponse(/* now= */ 0, INTERFACE_INDEX, mNetwork);
|
||||||
assertEquals(response.getInterfaceIndex(), -1);
|
assertEquals(INTERFACE_INDEX, response1.getInterfaceIndex());
|
||||||
|
|
||||||
|
final MdnsResponse response2 =
|
||||||
|
new MdnsResponse(/* now= */ 0, 1234 /* interfaceIndex */, mNetwork);
|
||||||
|
assertEquals(1234, response2.getInterfaceIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getInterfaceIndex_afterSet_returnsValue() {
|
public void testGetNetwork() {
|
||||||
MdnsResponse response = new MdnsResponse(/* now= */ 0);
|
final MdnsResponse response1 =
|
||||||
response.setInterfaceIndex(5);
|
new MdnsResponse(/* now= */ 0, INTERFACE_INDEX, null /* network */);
|
||||||
assertEquals(response.getInterfaceIndex(), 5);
|
assertNull(response1.getNetwork());
|
||||||
|
|
||||||
|
final MdnsResponse response2 =
|
||||||
|
new MdnsResponse(/* now= */ 0, 1234 /* interfaceIndex */, mNetwork);
|
||||||
|
assertEquals(mNetwork, response2.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -16,13 +16,16 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.mdns;
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import static com.android.server.connectivity.mdns.MdnsSocket.INTERFACE_INDEX_UNSPECIFIED;
|
||||||
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.net.Network;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
|
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
|
||||||
@@ -128,7 +131,7 @@ public class MdnsServiceInfoTest {
|
|||||||
"2001::1",
|
"2001::1",
|
||||||
List.of());
|
List.of());
|
||||||
|
|
||||||
assertEquals(info.getInterfaceIndex(), -1);
|
assertEquals(info.getInterfaceIndex(), INTERFACE_INDEX_UNSPECIFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -149,6 +152,41 @@ public class MdnsServiceInfoTest {
|
|||||||
assertEquals(info.getInterfaceIndex(), 20);
|
assertEquals(info.getInterfaceIndex(), 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetNetwork() {
|
||||||
|
final MdnsServiceInfo info1 =
|
||||||
|
new MdnsServiceInfo(
|
||||||
|
"my-mdns-service",
|
||||||
|
new String[] {"_googlecast", "_tcp"},
|
||||||
|
List.of(),
|
||||||
|
new String[] {"my-host", "local"},
|
||||||
|
12345,
|
||||||
|
"192.168.1.1",
|
||||||
|
"2001::1",
|
||||||
|
List.of(),
|
||||||
|
/* textEntries= */ null,
|
||||||
|
/* interfaceIndex= */ 20);
|
||||||
|
|
||||||
|
assertNull(info1.getNetwork());
|
||||||
|
|
||||||
|
final Network network = mock(Network.class);
|
||||||
|
final MdnsServiceInfo info2 =
|
||||||
|
new MdnsServiceInfo(
|
||||||
|
"my-mdns-service",
|
||||||
|
new String[] {"_googlecast", "_tcp"},
|
||||||
|
List.of(),
|
||||||
|
new String[] {"my-host", "local"},
|
||||||
|
12345,
|
||||||
|
"192.168.1.1",
|
||||||
|
"2001::1",
|
||||||
|
List.of(),
|
||||||
|
/* textEntries= */ null,
|
||||||
|
/* interfaceIndex= */ 20,
|
||||||
|
network);
|
||||||
|
|
||||||
|
assertEquals(network, info2.getNetwork());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parcelable_canBeParceledAndUnparceled() {
|
public void parcelable_canBeParceledAndUnparceled() {
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
@@ -165,7 +203,9 @@ public class MdnsServiceInfoTest {
|
|||||||
List.of(
|
List.of(
|
||||||
MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
|
MdnsServiceInfo.TextEntry.fromString("vn=Google Inc."),
|
||||||
MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max"),
|
MdnsServiceInfo.TextEntry.fromString("mn=Google Nest Hub Max"),
|
||||||
MdnsServiceInfo.TextEntry.fromString("test=")));
|
MdnsServiceInfo.TextEntry.fromString("test=")),
|
||||||
|
20 /* interfaceIndex */,
|
||||||
|
new Network(123));
|
||||||
|
|
||||||
beforeParcel.writeToParcel(parcel, 0);
|
beforeParcel.writeToParcel(parcel, 0);
|
||||||
parcel.setDataPosition(0);
|
parcel.setDataPosition(0);
|
||||||
@@ -179,6 +219,8 @@ public class MdnsServiceInfoTest {
|
|||||||
assertEquals(beforeParcel.getIpv4Address(), afterParcel.getIpv4Address());
|
assertEquals(beforeParcel.getIpv4Address(), afterParcel.getIpv4Address());
|
||||||
assertEquals(beforeParcel.getIpv6Address(), afterParcel.getIpv6Address());
|
assertEquals(beforeParcel.getIpv6Address(), afterParcel.getIpv6Address());
|
||||||
assertEquals(beforeParcel.getAttributes(), afterParcel.getAttributes());
|
assertEquals(beforeParcel.getAttributes(), afterParcel.getAttributes());
|
||||||
|
assertEquals(beforeParcel.getInterfaceIndex(), afterParcel.getInterfaceIndex());
|
||||||
|
assertEquals(beforeParcel.getNetwork(), afterParcel.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.net.InetAddresses;
|
import android.net.InetAddresses;
|
||||||
|
import android.net.Network;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
|
import com.android.server.connectivity.mdns.MdnsServiceInfo.TextEntry;
|
||||||
@@ -79,6 +80,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
private static final int INTERFACE_INDEX = 999;
|
private static final int INTERFACE_INDEX = 999;
|
||||||
private static final String SERVICE_TYPE = "_googlecast._tcp.local";
|
private static final String SERVICE_TYPE = "_googlecast._tcp.local";
|
||||||
private static final String[] SERVICE_TYPE_LABELS = TextUtils.split(SERVICE_TYPE, "\\.");
|
private static final String[] SERVICE_TYPE_LABELS = TextUtils.split(SERVICE_TYPE, "\\.");
|
||||||
|
private static final Network NETWORK = mock(Network.class);
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private MdnsServiceBrowserListener mockListenerOne;
|
private MdnsServiceBrowserListener mockListenerOne;
|
||||||
@@ -385,7 +387,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
|
|
||||||
private static void verifyServiceInfo(MdnsServiceInfo serviceInfo, String serviceName,
|
private static void verifyServiceInfo(MdnsServiceInfo serviceInfo, String serviceName,
|
||||||
String[] serviceType, String ipv4Address, String ipv6Address, int port,
|
String[] serviceType, String ipv4Address, String ipv6Address, int port,
|
||||||
List<String> subTypes, Map<String, String> attributes, int interfaceIndex) {
|
List<String> subTypes, Map<String, String> attributes, int interfaceIndex,
|
||||||
|
Network network) {
|
||||||
assertEquals(serviceName, serviceInfo.getServiceInstanceName());
|
assertEquals(serviceName, serviceInfo.getServiceInstanceName());
|
||||||
assertArrayEquals(serviceType, serviceInfo.getServiceType());
|
assertArrayEquals(serviceType, serviceInfo.getServiceType());
|
||||||
assertEquals(ipv4Address, serviceInfo.getIpv4Address());
|
assertEquals(ipv4Address, serviceInfo.getIpv4Address());
|
||||||
@@ -396,6 +399,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
assertEquals(attributes.get(key), serviceInfo.getAttributeByKey(key));
|
assertEquals(attributes.get(key), serviceInfo.getAttributeByKey(key));
|
||||||
}
|
}
|
||||||
assertEquals(interfaceIndex, serviceInfo.getInterfaceIndex());
|
assertEquals(interfaceIndex, serviceInfo.getInterfaceIndex());
|
||||||
|
assertEquals(network, serviceInfo.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -405,6 +409,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
MdnsResponse response = mock(MdnsResponse.class);
|
MdnsResponse response = mock(MdnsResponse.class);
|
||||||
when(response.getServiceInstanceName()).thenReturn("service-instance-1");
|
when(response.getServiceInstanceName()).thenReturn("service-instance-1");
|
||||||
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
|
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
|
||||||
|
doReturn(NETWORK).when(response).getNetwork();
|
||||||
when(response.isComplete()).thenReturn(false);
|
when(response.isComplete()).thenReturn(false);
|
||||||
|
|
||||||
client.processResponse(response);
|
client.processResponse(response);
|
||||||
@@ -417,7 +422,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
0 /* port */,
|
0 /* port */,
|
||||||
List.of() /* subTypes */,
|
List.of() /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
|
verify(mockListenerOne, never()).onServiceFound(any(MdnsServiceInfo.class));
|
||||||
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
|
verify(mockListenerOne, never()).onServiceUpdated(any(MdnsServiceInfo.class));
|
||||||
@@ -436,7 +442,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353,
|
5353,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
/* interfaceIndex= */ 20);
|
/* interfaceIndex= */ 20,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Process a second response with a different port and updated text attributes.
|
// Process a second response with a different port and updated text attributes.
|
||||||
@@ -447,7 +454,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354,
|
5354,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.singletonMap("key", "value"),
|
Collections.singletonMap("key", "value"),
|
||||||
/* interfaceIndex= */ 20);
|
/* interfaceIndex= */ 20,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(secondResponse);
|
client.processResponse(secondResponse);
|
||||||
|
|
||||||
// Verify onServiceNameDiscovered was called once for the initial response.
|
// Verify onServiceNameDiscovered was called once for the initial response.
|
||||||
@@ -460,7 +468,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
20 /* interfaceIndex */);
|
20 /* interfaceIndex */,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceFound was called once for the initial response.
|
// Verify onServiceFound was called once for the initial response.
|
||||||
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
||||||
@@ -471,6 +480,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
||||||
assertNull(initialServiceInfo.getAttributeByKey("key"));
|
assertNull(initialServiceInfo.getAttributeByKey("key"));
|
||||||
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
|
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
|
||||||
|
assertEquals(NETWORK, initialServiceInfo.getNetwork());
|
||||||
|
|
||||||
// Verify onServiceUpdated was called once for the second response.
|
// Verify onServiceUpdated was called once for the second response.
|
||||||
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
||||||
@@ -482,6 +492,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
||||||
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
|
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
|
||||||
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
|
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
|
||||||
|
assertEquals(NETWORK, updatedServiceInfo.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -497,7 +508,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353,
|
5353,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
/* interfaceIndex= */ 20);
|
/* interfaceIndex= */ 20,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Process a second response with a different port and updated text attributes.
|
// Process a second response with a different port and updated text attributes.
|
||||||
@@ -508,7 +520,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354,
|
5354,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.singletonMap("key", "value"),
|
Collections.singletonMap("key", "value"),
|
||||||
/* interfaceIndex= */ 20);
|
/* interfaceIndex= */ 20,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(secondResponse);
|
client.processResponse(secondResponse);
|
||||||
|
|
||||||
System.out.println("secondResponses ip"
|
System.out.println("secondResponses ip"
|
||||||
@@ -524,7 +537,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
20 /* interfaceIndex */);
|
20 /* interfaceIndex */,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceFound was called once for the initial response.
|
// Verify onServiceFound was called once for the initial response.
|
||||||
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
||||||
@@ -535,6 +549,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
assertEquals(initialServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
||||||
assertNull(initialServiceInfo.getAttributeByKey("key"));
|
assertNull(initialServiceInfo.getAttributeByKey("key"));
|
||||||
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
|
assertEquals(initialServiceInfo.getInterfaceIndex(), 20);
|
||||||
|
assertEquals(NETWORK, initialServiceInfo.getNetwork());
|
||||||
|
|
||||||
// Verify onServiceUpdated was called once for the second response.
|
// Verify onServiceUpdated was called once for the second response.
|
||||||
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
||||||
@@ -546,6 +561,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
assertEquals(updatedServiceInfo.getSubtypes(), Collections.singletonList("ABCDE"));
|
||||||
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
|
assertEquals(updatedServiceInfo.getAttributeByKey("key"), "value");
|
||||||
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
|
assertEquals(updatedServiceInfo.getInterfaceIndex(), 20);
|
||||||
|
assertEquals(NETWORK, updatedServiceInfo.getNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyServiceRemovedNoCallback(MdnsServiceBrowserListener listener) {
|
private void verifyServiceRemovedNoCallback(MdnsServiceBrowserListener listener) {
|
||||||
@@ -554,15 +570,17 @@ public class MdnsServiceTypeClientTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void verifyServiceRemovedCallback(MdnsServiceBrowserListener listener,
|
private void verifyServiceRemovedCallback(MdnsServiceBrowserListener listener,
|
||||||
String serviceName, String[] serviceType, int interfaceIndex) {
|
String serviceName, String[] serviceType, int interfaceIndex, Network network) {
|
||||||
verify(listener).onServiceRemoved(argThat(
|
verify(listener).onServiceRemoved(argThat(
|
||||||
info -> serviceName.equals(info.getServiceInstanceName())
|
info -> serviceName.equals(info.getServiceInstanceName())
|
||||||
&& Arrays.equals(serviceType, info.getServiceType())
|
&& Arrays.equals(serviceType, info.getServiceType())
|
||||||
&& info.getInterfaceIndex() == interfaceIndex));
|
&& info.getInterfaceIndex() == interfaceIndex
|
||||||
|
&& network.equals(info.getNetwork())));
|
||||||
verify(listener).onServiceNameRemoved(argThat(
|
verify(listener).onServiceNameRemoved(argThat(
|
||||||
info -> serviceName.equals(info.getServiceInstanceName())
|
info -> serviceName.equals(info.getServiceInstanceName())
|
||||||
&& Arrays.equals(serviceType, info.getServiceType())
|
&& Arrays.equals(serviceType, info.getServiceType())
|
||||||
&& info.getInterfaceIndex() == interfaceIndex));
|
&& info.getInterfaceIndex() == interfaceIndex
|
||||||
|
&& network.equals(info.getNetwork())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -580,11 +598,13 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
MdnsResponse response = mock(MdnsResponse.class);
|
MdnsResponse response = mock(MdnsResponse.class);
|
||||||
doReturn("goodbye-service").when(response).getServiceInstanceName();
|
doReturn("goodbye-service").when(response).getServiceInstanceName();
|
||||||
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
|
doReturn(INTERFACE_INDEX).when(response).getInterfaceIndex();
|
||||||
|
doReturn(NETWORK).when(response).getNetwork();
|
||||||
doReturn(true).when(response).isGoodbye();
|
doReturn(true).when(response).isGoodbye();
|
||||||
client.processResponse(response);
|
client.processResponse(response);
|
||||||
// Verify removed callback won't be called if the service is not existed.
|
// Verify removed callback won't be called if the service is not existed.
|
||||||
@@ -595,9 +615,9 @@ public class MdnsServiceTypeClientTests {
|
|||||||
doReturn(serviceName).when(response).getServiceInstanceName();
|
doReturn(serviceName).when(response).getServiceInstanceName();
|
||||||
client.processResponse(response);
|
client.processResponse(response);
|
||||||
verifyServiceRemovedCallback(
|
verifyServiceRemovedCallback(
|
||||||
mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
|
mockListenerOne, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, NETWORK);
|
||||||
verifyServiceRemovedCallback(
|
verifyServiceRemovedCallback(
|
||||||
mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
|
mockListenerTwo, serviceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX, NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -610,7 +630,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353,
|
5353,
|
||||||
/* subtype= */ "ABCDE",
|
/* subtype= */ "ABCDE",
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
|
client.startSendAndReceive(mockListenerOne, MdnsSearchOptions.getDefaultOptions());
|
||||||
@@ -625,7 +646,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceFound was called once for the existing response.
|
// Verify onServiceFound was called once for the existing response.
|
||||||
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
||||||
@@ -662,7 +684,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
MdnsResponse initialResponse =
|
MdnsResponse initialResponse =
|
||||||
createMockResponse(
|
createMockResponse(
|
||||||
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
||||||
Map.of(), INTERFACE_INDEX);
|
Map.of(), INTERFACE_INDEX, NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Clear the scheduled runnable.
|
// Clear the scheduled runnable.
|
||||||
@@ -696,7 +718,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
MdnsResponse initialResponse =
|
MdnsResponse initialResponse =
|
||||||
createMockResponse(
|
createMockResponse(
|
||||||
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
||||||
Map.of(), INTERFACE_INDEX);
|
Map.of(), INTERFACE_INDEX, NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Clear the scheduled runnable.
|
// Clear the scheduled runnable.
|
||||||
@@ -714,8 +736,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
firstMdnsTask.run();
|
firstMdnsTask.run();
|
||||||
|
|
||||||
// Verify removed callback was called.
|
// Verify removed callback was called.
|
||||||
verifyServiceRemovedCallback(
|
verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
|
||||||
mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
|
INTERFACE_INDEX, NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -736,7 +758,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
MdnsResponse initialResponse =
|
MdnsResponse initialResponse =
|
||||||
createMockResponse(
|
createMockResponse(
|
||||||
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
||||||
Map.of(), INTERFACE_INDEX);
|
Map.of(), INTERFACE_INDEX, NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Clear the scheduled runnable.
|
// Clear the scheduled runnable.
|
||||||
@@ -770,7 +792,7 @@ public class MdnsServiceTypeClientTests {
|
|||||||
MdnsResponse initialResponse =
|
MdnsResponse initialResponse =
|
||||||
createMockResponse(
|
createMockResponse(
|
||||||
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
serviceInstanceName, "192.168.1.1", 5353, List.of("ABCDE"),
|
||||||
Map.of(), INTERFACE_INDEX);
|
Map.of(), INTERFACE_INDEX, NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Clear the scheduled runnable.
|
// Clear the scheduled runnable.
|
||||||
@@ -781,8 +803,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
firstMdnsTask.run();
|
firstMdnsTask.run();
|
||||||
|
|
||||||
// Verify removed callback was called.
|
// Verify removed callback was called.
|
||||||
verifyServiceRemovedCallback(
|
verifyServiceRemovedCallback(mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS,
|
||||||
mockListenerOne, serviceInstanceName, SERVICE_TYPE_LABELS, INTERFACE_INDEX);
|
INTERFACE_INDEX, NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -801,7 +823,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353,
|
5353,
|
||||||
"ABCDE" /* subtype */,
|
"ABCDE" /* subtype */,
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(initialResponse);
|
client.processResponse(initialResponse);
|
||||||
|
|
||||||
// Process a second response which has ip address to make response become complete.
|
// Process a second response which has ip address to make response become complete.
|
||||||
@@ -812,7 +835,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353,
|
5353,
|
||||||
"ABCDE" /* subtype */,
|
"ABCDE" /* subtype */,
|
||||||
Collections.emptyMap(),
|
Collections.emptyMap(),
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(secondResponse);
|
client.processResponse(secondResponse);
|
||||||
|
|
||||||
// Process a third response with a different ip address, port and updated text attributes.
|
// Process a third response with a different ip address, port and updated text attributes.
|
||||||
@@ -823,7 +847,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354,
|
5354,
|
||||||
"ABCDE" /* subtype */,
|
"ABCDE" /* subtype */,
|
||||||
Collections.singletonMap("key", "value"),
|
Collections.singletonMap("key", "value"),
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
client.processResponse(thirdResponse);
|
client.processResponse(thirdResponse);
|
||||||
|
|
||||||
// Process the last response which is goodbye message.
|
// Process the last response which is goodbye message.
|
||||||
@@ -842,7 +867,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceFound was second called for the second response.
|
// Verify onServiceFound was second called for the second response.
|
||||||
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
inOrder.verify(mockListenerOne).onServiceFound(serviceInfoCaptor.capture());
|
||||||
@@ -854,7 +880,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5353 /* port */,
|
5353 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", null) /* attributes */,
|
Collections.singletonMap("key", null) /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceUpdated was third called for the third response.
|
// Verify onServiceUpdated was third called for the third response.
|
||||||
inOrder.verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
inOrder.verify(mockListenerOne).onServiceUpdated(serviceInfoCaptor.capture());
|
||||||
@@ -866,7 +893,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354 /* port */,
|
5354 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", "value") /* attributes */,
|
Collections.singletonMap("key", "value") /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceRemoved was called for the last response.
|
// Verify onServiceRemoved was called for the last response.
|
||||||
inOrder.verify(mockListenerOne).onServiceRemoved(serviceInfoCaptor.capture());
|
inOrder.verify(mockListenerOne).onServiceRemoved(serviceInfoCaptor.capture());
|
||||||
@@ -878,7 +906,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354 /* port */,
|
5354 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", "value") /* attributes */,
|
Collections.singletonMap("key", "value") /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
|
|
||||||
// Verify onServiceNameRemoved was called for the last response.
|
// Verify onServiceNameRemoved was called for the last response.
|
||||||
inOrder.verify(mockListenerOne).onServiceNameRemoved(serviceInfoCaptor.capture());
|
inOrder.verify(mockListenerOne).onServiceNameRemoved(serviceInfoCaptor.capture());
|
||||||
@@ -890,7 +919,8 @@ public class MdnsServiceTypeClientTests {
|
|||||||
5354 /* port */,
|
5354 /* port */,
|
||||||
Collections.singletonList("ABCDE") /* subTypes */,
|
Collections.singletonList("ABCDE") /* subTypes */,
|
||||||
Collections.singletonMap("key", "value") /* attributes */,
|
Collections.singletonMap("key", "value") /* attributes */,
|
||||||
INTERFACE_INDEX);
|
INTERFACE_INDEX,
|
||||||
|
NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifies that the right query was enqueued with the right delay, and send query by executing
|
// verifies that the right query was enqueued with the right delay, and send query by executing
|
||||||
@@ -962,26 +992,25 @@ public class MdnsServiceTypeClientTests {
|
|||||||
int port,
|
int port,
|
||||||
@NonNull List<String> subtypes,
|
@NonNull List<String> subtypes,
|
||||||
@NonNull Map<String, String> textAttributes,
|
@NonNull Map<String, String> textAttributes,
|
||||||
int interfaceIndex)
|
int interfaceIndex,
|
||||||
|
Network network)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String[] hostName = new String[]{"hostname"};
|
String[] hostName = new String[]{"hostname"};
|
||||||
MdnsServiceRecord serviceRecord = mock(MdnsServiceRecord.class);
|
MdnsServiceRecord serviceRecord = mock(MdnsServiceRecord.class);
|
||||||
when(serviceRecord.getServiceHost()).thenReturn(hostName);
|
when(serviceRecord.getServiceHost()).thenReturn(hostName);
|
||||||
when(serviceRecord.getServicePort()).thenReturn(port);
|
when(serviceRecord.getServicePort()).thenReturn(port);
|
||||||
|
|
||||||
MdnsResponse response = spy(new MdnsResponse(0));
|
MdnsResponse response = spy(new MdnsResponse(0, interfaceIndex, network));
|
||||||
|
|
||||||
MdnsInetAddressRecord inetAddressRecord = mock(MdnsInetAddressRecord.class);
|
MdnsInetAddressRecord inetAddressRecord = mock(MdnsInetAddressRecord.class);
|
||||||
if (host.contains(":")) {
|
if (host.contains(":")) {
|
||||||
when(inetAddressRecord.getInet6Address())
|
when(inetAddressRecord.getInet6Address())
|
||||||
.thenReturn((Inet6Address) Inet6Address.getByName(host));
|
.thenReturn((Inet6Address) Inet6Address.getByName(host));
|
||||||
response.setInet6AddressRecord(inetAddressRecord);
|
response.setInet6AddressRecord(inetAddressRecord);
|
||||||
response.setInterfaceIndex(interfaceIndex);
|
|
||||||
} else {
|
} else {
|
||||||
when(inetAddressRecord.getInet4Address())
|
when(inetAddressRecord.getInet4Address())
|
||||||
.thenReturn((Inet4Address) Inet4Address.getByName(host));
|
.thenReturn((Inet4Address) Inet4Address.getByName(host));
|
||||||
response.setInet4AddressRecord(inetAddressRecord);
|
response.setInet4AddressRecord(inetAddressRecord);
|
||||||
response.setInterfaceIndex(interfaceIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MdnsTextRecord textRecord = mock(MdnsTextRecord.class);
|
MdnsTextRecord textRecord = mock(MdnsTextRecord.class);
|
||||||
@@ -1011,10 +1040,10 @@ public class MdnsServiceTypeClientTests {
|
|||||||
int port,
|
int port,
|
||||||
@NonNull String subtype,
|
@NonNull String subtype,
|
||||||
@NonNull Map<String, String> textAttributes,
|
@NonNull Map<String, String> textAttributes,
|
||||||
int interfaceIndex)
|
int interfaceIndex,
|
||||||
|
Network network)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MdnsResponse response = new MdnsResponse(0);
|
MdnsResponse response = new MdnsResponse(0, interfaceIndex, network);
|
||||||
response.setInterfaceIndex(interfaceIndex);
|
|
||||||
|
|
||||||
// Set PTR record
|
// Set PTR record
|
||||||
final MdnsPointerRecord pointerRecord = new MdnsPointerRecord(
|
final MdnsPointerRecord pointerRecord = new MdnsPointerRecord(
|
||||||
|
|||||||
@@ -501,8 +501,7 @@ public class MdnsSocketClientTests {
|
|||||||
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(true);
|
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(true);
|
||||||
|
|
||||||
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
|
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
|
||||||
mdnsClient =
|
mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock) {
|
||||||
new MdnsSocketClient(mContext, mockMulticastLock) {
|
|
||||||
@Override
|
@Override
|
||||||
MdnsSocket createMdnsSocket(int port) {
|
MdnsSocket createMdnsSocket(int port) {
|
||||||
if (port == MdnsConstants.MDNS_PORT) {
|
if (port == MdnsConstants.MDNS_PORT) {
|
||||||
@@ -525,8 +524,7 @@ public class MdnsSocketClientTests {
|
|||||||
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(false);
|
//MdnsConfigsFlagsImpl.allowNetworkInterfaceIndexPropagation.override(false);
|
||||||
|
|
||||||
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
|
when(mockMulticastSocket.getInterfaceIndex()).thenReturn(21);
|
||||||
mdnsClient =
|
mdnsClient = new MdnsSocketClient(mContext, mockMulticastLock) {
|
||||||
new MdnsSocketClient(mContext, mockMulticastLock) {
|
|
||||||
@Override
|
@Override
|
||||||
MdnsSocket createMdnsSocket(int port) {
|
MdnsSocket createMdnsSocket(int port) {
|
||||||
if (port == MdnsConstants.MDNS_PORT) {
|
if (port == MdnsConstants.MDNS_PORT) {
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.connectivity.mdns;
|
||||||
|
|
||||||
|
import static com.android.testutils.ContextUtils.mockService;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
|
import android.net.INetd;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.TetheringManager;
|
||||||
|
import android.net.TetheringManager.TetheringEventCallback;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
|
||||||
|
import com.android.net.module.util.ArrayTrackRecord;
|
||||||
|
import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies;
|
||||||
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
|
import com.android.testutils.DevSdkIgnoreRunner;
|
||||||
|
import com.android.testutils.HandlerUtils;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
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 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 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 Network TEST_NETWORK = new Network(123);
|
||||||
|
private static final Network LOCAL_NETWORK = new Network(INetd.LOCAL_NET_ID);
|
||||||
|
|
||||||
|
@Mock private Context mContext;
|
||||||
|
@Mock private Dependencies mDeps;
|
||||||
|
@Mock private ConnectivityManager mCm;
|
||||||
|
@Mock private TetheringManager mTm;
|
||||||
|
@Mock private NetworkInterfaceWrapper mTestNetworkIfaceWrapper;
|
||||||
|
@Mock private NetworkInterfaceWrapper mLocalOnlyIfaceWrapper;
|
||||||
|
@Mock private NetworkInterfaceWrapper mTetheredIfaceWrapper;
|
||||||
|
private Handler mHandler;
|
||||||
|
private MdnsSocketProvider mSocketProvider;
|
||||||
|
private NetworkCallback mNetworkCallback;
|
||||||
|
private TetheringEventCallback mTetheringEventCallback;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws IOException {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mockService(mContext, ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mCm);
|
||||||
|
mockService(mContext, TetheringManager.class, Context.TETHERING_SERVICE, mTm);
|
||||||
|
doReturn(true).when(mDeps).canScanOnInterface(any());
|
||||||
|
doReturn(mTestNetworkIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TEST_IFACE_NAME);
|
||||||
|
doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
|
||||||
|
.getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
|
||||||
|
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
|
||||||
|
doReturn(mock(MdnsInterfaceSocket.class))
|
||||||
|
.when(mDeps).createMdnsInterfaceSocket(any(), anyInt());
|
||||||
|
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
|
||||||
|
thread.start();
|
||||||
|
mHandler = new Handler(thread.getLooper());
|
||||||
|
|
||||||
|
final ArgumentCaptor<NetworkCallback> nwCallbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(NetworkCallback.class);
|
||||||
|
final ArgumentCaptor<TetheringEventCallback> teCallbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(TetheringEventCallback.class);
|
||||||
|
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps);
|
||||||
|
mHandler.post(mSocketProvider::startMonitoringSockets);
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mCm).registerNetworkCallback(any(), nwCallbackCaptor.capture(), any());
|
||||||
|
verify(mTm).registerTetheringEventCallback(any(), teCallbackCaptor.capture());
|
||||||
|
|
||||||
|
mNetworkCallback = nwCallbackCaptor.getValue();
|
||||||
|
mTetheringEventCallback = teCallbackCaptor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSocketCallback implements MdnsSocketProvider.SocketCallback {
|
||||||
|
private class SocketEvent {
|
||||||
|
public final Network mNetwork;
|
||||||
|
public final List<LinkAddress> mAddresses;
|
||||||
|
|
||||||
|
SocketEvent(Network network, List<LinkAddress> addresses) {
|
||||||
|
mNetwork = network;
|
||||||
|
mAddresses = Collections.unmodifiableList(addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SocketCreatedEvent extends SocketEvent {
|
||||||
|
SocketCreatedEvent(Network nw, List<LinkAddress> addresses) {
|
||||||
|
super(nw, addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InterfaceDestroyedEvent extends SocketEvent {
|
||||||
|
InterfaceDestroyedEvent(Network nw, List<LinkAddress> addresses) {
|
||||||
|
super(nw, addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AddressesChangedEvent extends SocketEvent {
|
||||||
|
AddressesChangedEvent(Network nw, List<LinkAddress> addresses) {
|
||||||
|
super(nw, addresses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayTrackRecord<SocketEvent>.ReadHead mHistory =
|
||||||
|
new ArrayTrackRecord<SocketEvent>().newReadHead();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketCreated(Network network, MdnsInterfaceSocket socket,
|
||||||
|
List<LinkAddress> addresses) {
|
||||||
|
mHistory.add(new SocketCreatedEvent(network, addresses));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInterfaceDestroyed(Network network, MdnsInterfaceSocket socket) {
|
||||||
|
mHistory.add(new InterfaceDestroyedEvent(network, List.of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddressesChanged(Network network, List<LinkAddress> addresses) {
|
||||||
|
mHistory.add(new AddressesChangedEvent(network, addresses));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectedSocketCreatedForNetwork(Network network, List<LinkAddress> addresses) {
|
||||||
|
final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
|
||||||
|
assertNotNull(event);
|
||||||
|
assertTrue(event instanceof SocketCreatedEvent);
|
||||||
|
assertEquals(network, event.mNetwork);
|
||||||
|
assertEquals(addresses, event.mAddresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectedInterfaceDestroyedForNetwork(Network network) {
|
||||||
|
final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
|
||||||
|
assertNotNull(event);
|
||||||
|
assertTrue(event instanceof InterfaceDestroyedEvent);
|
||||||
|
assertEquals(network, event.mNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectedAddressesChangedForNetwork(Network network,
|
||||||
|
List<LinkAddress> addresses) {
|
||||||
|
final SocketEvent event = mHistory.poll(DEFAULT_TIMEOUT, c -> true);
|
||||||
|
assertNotNull(event);
|
||||||
|
assertTrue(event instanceof AddressesChangedEvent);
|
||||||
|
assertEquals(network, event.mNetwork);
|
||||||
|
assertEquals(event.mAddresses, addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void expectedNoCallback() {
|
||||||
|
final SocketEvent event = mHistory.poll(NO_CALLBACK_TIMEOUT, c -> true);
|
||||||
|
assertNull(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSocketRequestAndUnrequestSocket() {
|
||||||
|
final TestSocketCallback testCallback1 = new TestSocketCallback();
|
||||||
|
mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
|
||||||
|
final LinkProperties testLp = new LinkProperties();
|
||||||
|
testLp.setInterfaceName(TEST_IFACE_NAME);
|
||||||
|
testLp.setLinkAddresses(List.of(LINKADDRV4));
|
||||||
|
mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mTestNetworkIfaceWrapper).getNetworkInterface();
|
||||||
|
testCallback1.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
|
||||||
|
|
||||||
|
final TestSocketCallback testCallback2 = new TestSocketCallback();
|
||||||
|
mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
|
||||||
|
|
||||||
|
final TestSocketCallback testCallback3 = new TestSocketCallback();
|
||||||
|
mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
|
||||||
|
|
||||||
|
mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
|
||||||
|
List.of(LOCAL_ONLY_IFACE_NAME)));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedSocketCreatedForNetwork(LOCAL_NETWORK, List.of());
|
||||||
|
|
||||||
|
mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
|
||||||
|
List.of(TETHERED_IFACE_NAME)));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mTetheredIfaceWrapper).getNetworkInterface();
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedSocketCreatedForNetwork(LOCAL_NETWORK, List.of());
|
||||||
|
|
||||||
|
mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback1));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedNoCallback();
|
||||||
|
|
||||||
|
mHandler.post(() -> mNetworkCallback.onLost(TEST_NETWORK));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
|
||||||
|
testCallback3.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
|
||||||
|
|
||||||
|
mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedInterfaceDestroyedForNetwork(LOCAL_NETWORK);
|
||||||
|
|
||||||
|
mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback3));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback1.expectedNoCallback();
|
||||||
|
testCallback2.expectedNoCallback();
|
||||||
|
testCallback3.expectedNoCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddressesChanged() throws Exception {
|
||||||
|
final TestSocketCallback testCallback = new TestSocketCallback();
|
||||||
|
mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
testCallback.expectedNoCallback();
|
||||||
|
|
||||||
|
final LinkProperties testLp = new LinkProperties();
|
||||||
|
testLp.setInterfaceName(TEST_IFACE_NAME);
|
||||||
|
testLp.setLinkAddresses(List.of(LINKADDRV4));
|
||||||
|
mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
|
||||||
|
testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
|
||||||
|
|
||||||
|
final LinkProperties newTestLp = new LinkProperties();
|
||||||
|
newTestLp.setInterfaceName(TEST_IFACE_NAME);
|
||||||
|
newTestLp.setLinkAddresses(List.of(LINKADDRV4, LINKADDRV6));
|
||||||
|
mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
|
||||||
|
HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
|
||||||
|
verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
|
||||||
|
testCallback.expectedAddressesChangedForNetwork(
|
||||||
|
TEST_NETWORK, List.of(LINKADDRV4, LINKADDRV6));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user