Merge "Take the multicast lock on mDNS usage" am: 5f921c4292

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

Change-Id: I44877aa4d199ad9bf9ed915613f0ad0bc7b5aa7d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Remi NGUYEN VAN
2023-06-06 08:44:19 +00:00
committed by Automerger Merge Worker
5 changed files with 569 additions and 49 deletions

View File

@@ -17,6 +17,8 @@
package com.android.server;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.nsd.NsdManager.MDNS_DISCOVERY_MANAGER_EVENT;
import static android.net.nsd.NsdManager.MDNS_SERVICE_EVENT;
import static android.net.nsd.NsdManager.RESOLVE_SERVICE_SUCCEEDED;
@@ -27,6 +29,7 @@ import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -45,6 +48,7 @@ import android.net.nsd.INsdServiceConnector;
import android.net.nsd.MDnsManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -53,7 +57,9 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -62,6 +68,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.InetAddressUtils;
import com.android.net.module.util.PermissionUtils;
@@ -69,6 +76,7 @@ import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.ExecutorProvider;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
import com.android.server.connectivity.mdns.MdnsMultinetworkSocketClient;
import com.android.server.connectivity.mdns.MdnsSearchOptions;
import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
@@ -141,6 +149,14 @@ public class NsdService extends INsdManager.Stub {
"mdns_advertiser_allowlist_";
private static final String MDNS_ALLOWLIST_FLAG_SUFFIX = "_version";
@VisibleForTesting
static final String MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
"mdns_config_running_app_active_importance_cutoff";
@VisibleForTesting
static final int DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF =
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
private final int mRunningAppActiveImportanceCutoff;
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final long CLEANUP_DELAY_MS = 10000;
private static final int IFACE_IDX_ANY = 0;
@@ -175,6 +191,16 @@ public class NsdService extends INsdManager.Stub {
/* A map from unique id to client info */
private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
// Note this is not final to avoid depending on the Wi-Fi service starting before NsdService
@Nullable
private WifiManager.MulticastLock mHeldMulticastLock;
// Fulfilled network requests that require the Wi-Fi lock: key is the obtained Network
// (non-null), value is the requested Network (nullable)
@NonNull
private final ArraySet<Network> mWifiLockRequiredNetworks = new ArraySet<>();
@NonNull
private final ArraySet<Integer> mRunningAppActiveUids = new ArraySet<>();
private final long mCleanupDelayMs;
private static final int INVALID_ID = 0;
@@ -299,6 +325,104 @@ public class NsdService extends INsdManager.Stub {
}
}
private class SocketRequestMonitor implements MdnsSocketProvider.SocketRequestMonitor {
@Override
public void onSocketRequestFulfilled(@Nullable Network socketNetwork,
@NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {
// The network may be null for Wi-Fi SoftAp interfaces (tethering), but there is no APF
// filtering on such interfaces, so taking the multicast lock is not necessary to
// disable APF filtering of multicast.
if (socketNetwork == null
|| !CollectionUtils.contains(transports, TRANSPORT_WIFI)
|| CollectionUtils.contains(transports, TRANSPORT_VPN)) {
return;
}
if (mWifiLockRequiredNetworks.add(socketNetwork)) {
updateMulticastLock();
}
}
@Override
public void onSocketDestroyed(@Nullable Network socketNetwork,
@NonNull MdnsInterfaceSocket socket) {
if (mWifiLockRequiredNetworks.remove(socketNetwork)) {
updateMulticastLock();
}
}
}
private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
private final Handler mHandler;
private UidImportanceListener(Handler handler) {
mHandler = handler;
}
@Override
public void onUidImportance(int uid, int importance) {
mHandler.post(() -> handleUidImportanceChanged(uid, importance));
}
}
private void handleUidImportanceChanged(int uid, int importance) {
// Lower importance values are more "important"
final boolean modified = importance <= mRunningAppActiveImportanceCutoff
? mRunningAppActiveUids.add(uid)
: mRunningAppActiveUids.remove(uid);
if (modified) {
updateMulticastLock();
}
}
/**
* Take or release the lock based on updated internal state.
*
* This determines whether the lock needs to be held based on
* {@link #mWifiLockRequiredNetworks}, {@link #mRunningAppActiveUids} and
* {@link ClientInfo#mClientRequests}, so it must be called after any of the these have been
* updated.
*/
private void updateMulticastLock() {
final int needsLockUid = getMulticastLockNeededUid();
if (needsLockUid >= 0 && mHeldMulticastLock == null) {
final WifiManager wm = mContext.getSystemService(WifiManager.class);
if (wm == null) {
Log.wtf(TAG, "Got a TRANSPORT_WIFI network without WifiManager");
return;
}
mHeldMulticastLock = wm.createMulticastLock(TAG);
mHeldMulticastLock.acquire();
mServiceLogs.log("Taking multicast lock for uid " + needsLockUid);
} else if (needsLockUid < 0 && mHeldMulticastLock != null) {
mHeldMulticastLock.release();
mHeldMulticastLock = null;
mServiceLogs.log("Released multicast lock");
}
}
/**
* @return The UID of an app requiring the multicast lock, or -1 if none.
*/
private int getMulticastLockNeededUid() {
if (mWifiLockRequiredNetworks.size() == 0) {
// Return early if NSD is not active, or not on any relevant network
return -1;
}
for (int i = 0; i < mIdToClientInfoMap.size(); i++) {
final ClientInfo clientInfo = mIdToClientInfoMap.valueAt(i);
if (!mRunningAppActiveUids.contains(clientInfo.mUid)) {
// Ignore non-active UIDs
continue;
}
if (clientInfo.hasAnyJavaBackendRequestForNetworks(mWifiLockRequiredNetworks)) {
return clientInfo.mUid;
}
}
return -1;
}
/**
* Data class of mdns service callback information.
*/
@@ -404,7 +528,7 @@ public class NsdService extends INsdManager.Stub {
try {
cb.asBinder().linkToDeath(arg.connector, 0);
final String tag = "Client" + arg.uid + "-" + mClientNumberId++;
cInfo = new ClientInfo(cb, arg.useJavaBackend,
cInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend,
mServiceLogs.forSubComponent(tag));
mClients.put(arg.connector, cInfo);
} catch (RemoteException e) {
@@ -529,9 +653,11 @@ public class NsdService extends INsdManager.Stub {
}
private void storeAdvertiserRequestMap(int clientId, int globalId,
ClientInfo clientInfo) {
clientInfo.mClientRequests.put(clientId, new AdvertiserClientRequest(globalId));
ClientInfo clientInfo, @Nullable Network requestedNetwork) {
clientInfo.mClientRequests.put(clientId,
new AdvertiserClientRequest(globalId, requestedNetwork));
mIdToClientInfoMap.put(globalId, clientInfo);
updateMulticastLock();
}
private void removeRequestMap(int clientId, int globalId, ClientInfo clientInfo) {
@@ -544,14 +670,17 @@ public class NsdService extends INsdManager.Stub {
maybeScheduleStop();
} else {
maybeStopMonitoringSocketsIfNoActiveRequest();
updateMulticastLock();
}
}
private void storeDiscoveryManagerRequestMap(int clientId, int globalId,
MdnsListener listener, ClientInfo clientInfo) {
MdnsListener listener, ClientInfo clientInfo,
@Nullable Network requestedNetwork) {
clientInfo.mClientRequests.put(clientId,
new DiscoveryManagerRequest(globalId, listener));
new DiscoveryManagerRequest(globalId, listener, requestedNetwork));
mIdToClientInfoMap.put(globalId, clientInfo);
updateMulticastLock();
}
/**
@@ -628,7 +757,8 @@ public class NsdService extends INsdManager.Stub {
}
mMdnsDiscoveryManager.registerListener(
listenServiceType, listener, optionsBuilder.build());
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo,
info.getNetwork());
clientInfo.onDiscoverServicesStarted(clientId, info);
clientInfo.log("Register a DiscoveryListener " + id
+ " for service type:" + listenServiceType);
@@ -728,7 +858,8 @@ public class NsdService extends INsdManager.Stub {
// Name._subtype._sub._type._tcp, which is incorrect
// (it should be Name._type._tcp).
mAdvertiser.addService(id, serviceInfo, typeSubtype.second);
storeAdvertiserRequestMap(clientId, id, clientInfo);
storeAdvertiserRequestMap(clientId, id, clientInfo,
serviceInfo.getNetwork());
} else {
maybeStartDaemon();
if (registerService(id, serviceInfo)) {
@@ -818,7 +949,8 @@ public class NsdService extends INsdManager.Stub {
.build();
mMdnsDiscoveryManager.registerListener(
resolveServiceType, listener, options);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo,
info.getNetwork());
clientInfo.log("Register a ResolutionListener " + id
+ " for service type:" + resolveServiceType);
} else {
@@ -912,7 +1044,8 @@ public class NsdService extends INsdManager.Stub {
.build();
mMdnsDiscoveryManager.registerListener(
resolveServiceType, listener, options);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo,
info.getNetwork());
clientInfo.log("Register a ServiceInfoListener " + id
+ " for service type:" + resolveServiceType);
break;
@@ -1389,10 +1522,20 @@ public class NsdService extends INsdManager.Stub {
mDeps = deps;
mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper(),
LOGGER.forSubComponent("MdnsSocketProvider"));
LOGGER.forSubComponent("MdnsSocketProvider"), new SocketRequestMonitor());
// Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
// address events are received.
handler.post(mMdnsSocketProvider::startNetLinkMonitor);
// NsdService is started after ActivityManager (startOtherServices in SystemServer, vs.
// startBootstrapServices).
mRunningAppActiveImportanceCutoff = mDeps.getDeviceConfigInt(
MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF,
DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF);
final ActivityManager am = ctx.getSystemService(ActivityManager.class);
am.addOnUidImportanceListener(new UidImportanceListener(handler),
mRunningAppActiveImportanceCutoff);
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(),
@@ -1471,8 +1614,23 @@ public class NsdService extends INsdManager.Stub {
* @see MdnsSocketProvider
*/
public MdnsSocketProvider makeMdnsSocketProvider(@NonNull Context context,
@NonNull Looper looper, @NonNull SharedLog sharedLog) {
return new MdnsSocketProvider(context, looper, sharedLog);
@NonNull Looper looper, @NonNull SharedLog sharedLog,
@NonNull MdnsSocketProvider.SocketRequestMonitor socketCreationCallback) {
return new MdnsSocketProvider(context, looper, sharedLog, socketCreationCallback);
}
/**
* @see DeviceConfig#getInt(String, String, int)
*/
public int getDeviceConfigInt(@NonNull String config, int defaultValue) {
return DeviceConfig.getInt(NAMESPACE_TETHERING, config, defaultValue);
}
/**
* @see Binder#getCallingUid()
*/
public int getCallingUid() {
return Binder.getCallingUid();
}
}
@@ -1626,7 +1784,7 @@ public class NsdService extends INsdManager.Stub {
final INsdServiceConnector connector = new NsdServiceConnector();
mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(NsdManager.REGISTER_CLIENT,
new ConnectorArgs((NsdServiceConnector) connector, cb, useJavaBackend,
Binder.getCallingUid())));
mDeps.getCallingUid())));
return connector;
}
@@ -1857,18 +2015,34 @@ public class NsdService extends INsdManager.Stub {
}
}
private static class AdvertiserClientRequest extends ClientRequest {
private AdvertiserClientRequest(int globalId) {
private abstract static class JavaBackendClientRequest extends ClientRequest {
@Nullable
private final Network mRequestedNetwork;
private JavaBackendClientRequest(int globalId, @Nullable Network requestedNetwork) {
super(globalId);
mRequestedNetwork = requestedNetwork;
}
@Nullable
public Network getRequestedNetwork() {
return mRequestedNetwork;
}
}
private static class DiscoveryManagerRequest extends ClientRequest {
private static class AdvertiserClientRequest extends JavaBackendClientRequest {
private AdvertiserClientRequest(int globalId, @Nullable Network requestedNetwork) {
super(globalId, requestedNetwork);
}
}
private static class DiscoveryManagerRequest extends JavaBackendClientRequest {
@NonNull
private final MdnsListener mListener;
private DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener) {
super(globalId);
private DiscoveryManagerRequest(int globalId, @NonNull MdnsListener listener,
@Nullable Network requestedNetwork) {
super(globalId, requestedNetwork);
mListener = listener;
}
}
@@ -1886,13 +2060,16 @@ public class NsdService extends INsdManager.Stub {
// The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsPreSClient = false;
private final int mUid;
// The flag of using java backend if the client's target SDK >= U
private final boolean mUseJavaBackend;
// Store client logs
private final SharedLog mClientLogs;
private ClientInfo(INsdManagerCallback cb, boolean useJavaBackend, SharedLog sharedLog) {
private ClientInfo(INsdManagerCallback cb, int uid, boolean useJavaBackend,
SharedLog sharedLog) {
mCb = cb;
mUid = uid;
mUseJavaBackend = useJavaBackend;
mClientLogs = sharedLog;
mClientLogs.log("New client. useJavaBackend=" + useJavaBackend);
@@ -1903,6 +2080,8 @@ public class NsdService extends INsdManager.Stub {
StringBuilder sb = new StringBuilder();
sb.append("mResolvedService ").append(mResolvedService).append("\n");
sb.append("mIsLegacy ").append(mIsPreSClient).append("\n");
sb.append("mUseJavaBackend ").append(mUseJavaBackend).append("\n");
sb.append("mUid ").append(mUid).append("\n");
for (int i = 0; i < mClientRequests.size(); i++) {
int clientID = mClientRequests.keyAt(i);
sb.append("clientId ")
@@ -1974,6 +2153,26 @@ public class NsdService extends INsdManager.Stub {
}
}
mClientRequests.clear();
updateMulticastLock();
}
/**
* Returns true if this client has any Java backend request that requests one of the given
* networks.
*/
boolean hasAnyJavaBackendRequestForNetworks(@NonNull ArraySet<Network> networks) {
for (int i = 0; i < mClientRequests.size(); i++) {
final ClientRequest req = mClientRequests.valueAt(i);
if (!(req instanceof JavaBackendClientRequest)) {
continue;
}
final Network reqNetwork = ((JavaBackendClientRequest) mClientRequests.valueAt(i))
.getRequestedNetwork();
if (MdnsUtils.isAnyNetworkMatched(reqNetwork, networks)) {
return true;
}
}
return false;
}
// mClientRequests is a sparse array of listener id -> ClientRequest. For a given

View File

@@ -97,6 +97,8 @@ public class MdnsSocketProvider {
// the netlink monitor is never stop and the old states must be kept.
private final SparseArray<LinkProperties> mIfaceIdxToLinkProperties = new SparseArray<>();
private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
@NonNull
private final SocketRequestMonitor mSocketRequestMonitor;
private boolean mMonitoringSockets = false;
private boolean mRequestStop = false;
private String mWifiP2pTetherInterface = null;
@@ -155,17 +157,20 @@ public class MdnsSocketProvider {
}
public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
@NonNull SharedLog sharedLog) {
this(context, looper, new Dependencies(), sharedLog);
@NonNull SharedLog sharedLog,
@NonNull SocketRequestMonitor socketRequestMonitor) {
this(context, looper, new Dependencies(), sharedLog, socketRequestMonitor);
}
MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
@NonNull Dependencies deps, @NonNull SharedLog sharedLog) {
@NonNull Dependencies deps, @NonNull SharedLog sharedLog,
@NonNull SocketRequestMonitor socketRequestMonitor) {
mContext = context;
mLooper = looper;
mHandler = new Handler(looper);
mDependencies = deps;
mSharedLog = sharedLog;
mSocketRequestMonitor = socketRequestMonitor;
mNetworkCallback = new NetworkCallback() {
@Override
public void onLost(Network network) {
@@ -312,10 +317,12 @@ public class MdnsSocketProvider {
private static class SocketInfo {
final MdnsInterfaceSocket mSocket;
final List<LinkAddress> mAddresses;
final int[] mTransports;
SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses) {
SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses, int[] transports) {
mSocket = socket;
mAddresses = new ArrayList<>(addresses);
mTransports = transports;
}
}
@@ -498,9 +505,13 @@ public class MdnsSocketProvider {
if (networkKey == LOCAL_NET) {
transports = new int[0];
} else {
transports = mActiveNetworksTransports.get(((NetworkAsKey) networkKey).mNetwork);
if (transports == null) {
final int[] knownTransports =
mActiveNetworksTransports.get(((NetworkAsKey) networkKey).mNetwork);
if (knownTransports != null) {
transports = knownTransports;
} else {
Log.wtf(TAG, "transports is missing for key: " + networkKey);
transports = new int[0];
}
}
if (networkInterface == null || !isMdnsCapableInterface(networkInterface, transports)) {
@@ -512,21 +523,22 @@ public class MdnsSocketProvider {
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
final List<LinkAddress> addresses = lp.getLinkAddresses();
// TODO: technically transport types are mutable, although generally not in ways that
// would meaningfully impact the logic using it here. Consider updating logic to
// support transports being added/removed.
final SocketInfo socketInfo = new SocketInfo(socket, addresses, transports);
if (networkKey == LOCAL_NET) {
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
mTetherInterfaceSockets.put(interfaceName, socketInfo);
} else {
mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
new SocketInfo(socket, addresses));
mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork, socketInfo);
}
// Try to join IPv4/IPv6 group.
socket.joinGroup(addresses);
// Notify the listeners which need this socket.
if (networkKey == LOCAL_NET) {
notifySocketCreated(null /* network */, socket, addresses);
} else {
notifySocketCreated(((NetworkAsKey) networkKey).mNetwork, socket, addresses);
}
final Network network =
networkKey == LOCAL_NET ? null : ((NetworkAsKey) networkKey).mNetwork;
notifySocketCreated(network, socketInfo);
} catch (IOException e) {
mSharedLog.e("Create socket failed ifName:" + interfaceName, e);
}
@@ -568,6 +580,7 @@ public class MdnsSocketProvider {
socketInfo.mSocket.destroy();
notifyInterfaceDestroyed(network, socketInfo.mSocket);
mSocketRequestMonitor.onSocketDestroyed(network, socketInfo.mSocket);
mSharedLog.log("Remove socket on net:" + network);
}
@@ -576,15 +589,18 @@ public class MdnsSocketProvider {
if (socketInfo == null) return;
socketInfo.mSocket.destroy();
notifyInterfaceDestroyed(null /* network */, socketInfo.mSocket);
mSocketRequestMonitor.onSocketDestroyed(null /* network */, socketInfo.mSocket);
mSharedLog.log("Remove socket on ifName:" + interfaceName);
}
private void notifySocketCreated(Network network, MdnsInterfaceSocket socket,
List<LinkAddress> addresses) {
private void notifySocketCreated(Network network, SocketInfo socketInfo) {
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);
mCallbacksToRequestedNetworks.keyAt(i).onSocketCreated(network, socketInfo.mSocket,
socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(network, socketInfo.mSocket,
socketInfo.mTransports);
}
}
}
@@ -622,6 +638,8 @@ public class MdnsSocketProvider {
} else {
// Notify the socket for requested network.
cb.onSocketCreated(network, socketInfo.mSocket, socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(network, socketInfo.mSocket,
socketInfo.mTransports);
}
}
@@ -636,6 +654,8 @@ public class MdnsSocketProvider {
// Notify the socket for requested network.
cb.onSocketCreated(
null /* network */, socketInfo.mSocket, socketInfo.mAddresses);
mSocketRequestMonitor.onSocketRequestFulfilled(null /* socketNetwork */,
socketInfo.mSocket, socketInfo.mTransports);
}
}
@@ -690,6 +710,7 @@ public class MdnsSocketProvider {
if (matchRequestedNetwork(network)) continue;
final SocketInfo info = mNetworkSockets.removeAt(i);
info.mSocket.destroy();
mSocketRequestMonitor.onSocketDestroyed(network, info.mSocket);
mSharedLog.log("Remove socket on net:" + network + " after unrequestSocket");
}
@@ -699,6 +720,7 @@ public class MdnsSocketProvider {
for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
info.mSocket.destroy();
mSocketRequestMonitor.onSocketDestroyed(null /* network */, info.mSocket);
mSharedLog.log("Remove socket on ifName:" + mTetherInterfaceSockets.keyAt(i)
+ " after unrequestSocket");
}
@@ -709,19 +731,61 @@ public class MdnsSocketProvider {
}
/*** Callbacks for listening socket changes */
/**
* Callback used to register socket requests.
*/
public interface SocketCallback {
/*** Notify the socket is created */
/**
* Notify the socket was created for the registered request.
*
* This may be called immediately when the request is registered with an existing socket,
* if it had been created previously for other requests.
*/
default void onSocketCreated(@Nullable Network network, @NonNull MdnsInterfaceSocket socket,
@NonNull List<LinkAddress> addresses) {}
/*** Notify the interface is destroyed */
/**
* Notify that the interface was destroyed, so the provided socket cannot be used anymore.
*
* This indicates that although the socket was still requested, it had to be destroyed.
*/
default void onInterfaceDestroyed(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket) {}
/*** Notify the addresses is changed on the network */
/**
* Notify the interface addresses have changed for the network.
*/
default void onAddressesChanged(@Nullable Network network,
@NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {}
}
/**
* Global callback indicating when sockets are created or destroyed for requests.
*/
public interface SocketRequestMonitor {
/**
* Indicates that the socket was used to fulfill the request of one requester.
*
* There is always at most one socket created for each interface. The interface is available
* in {@link MdnsInterfaceSocket#getInterface()}.
* @param socketNetwork The network of the socket interface, if any.
* @param socket The socket that was provided to a requester.
* @param transports Array of TRANSPORT_* from {@link NetworkCapabilities}. Empty if the
* interface is not part of a network with known transports.
*/
default void onSocketRequestFulfilled(@Nullable Network socketNetwork,
@NonNull MdnsInterfaceSocket socket, @NonNull int[] transports) {}
/**
* Indicates that a previously created socket was destroyed.
*
* @param socketNetwork The network of the socket interface, if any.
* @param socket The destroyed socket.
*/
default void onSocketDestroyed(@Nullable Network socketNetwork,
@NonNull MdnsInterfaceSocket socket) {}
}
private interface NetworkKey {
}

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
import android.os.Handler;
import android.util.ArraySet;
import com.android.server.connectivity.mdns.MdnsConstants;
import com.android.server.connectivity.mdns.MdnsRecord;
@@ -129,12 +130,21 @@ public class MdnsUtils {
return false;
}
/*** Check whether the target network is matched current network */
/*** Check whether the target network matches the current network */
public static boolean isNetworkMatched(@Nullable Network targetNetwork,
@Nullable Network currentNetwork) {
return targetNetwork == null || targetNetwork.equals(currentNetwork);
}
/*** Check whether the target network matches any of the current networks */
public static boolean isAnyNetworkMatched(@Nullable Network targetNetwork,
ArraySet<Network> currentNetworks) {
if (targetNetwork == null) {
return !currentNetworks.isEmpty();
}
return currentNetworks.contains(targetNetwork);
}
/**
* Truncate a service name to up to maxLength UTF-8 bytes.
*/

View File

@@ -16,13 +16,21 @@
package com.android.server;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND;
import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER;
import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS;
import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR;
import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING;
import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF;
import static com.android.server.NsdService.parseTypeAndSubtype;
import static com.android.testutils.ContextUtils.mockService;
@@ -42,6 +50,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -51,6 +60,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManager.OnUidImportanceListener;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.Context;
@@ -68,7 +79,9 @@ import android.net.nsd.NsdManager;
import android.net.nsd.NsdManager.DiscoveryListener;
import android.net.nsd.NsdManager.RegistrationListener;
import android.net.nsd.NsdManager.ResolveListener;
import android.net.nsd.NsdManager.ServiceInfoCallback;
import android.net.nsd.NsdServiceInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -85,10 +98,12 @@ import androidx.test.filters.SmallTest;
import com.android.server.NsdService.Dependencies;
import com.android.server.connectivity.mdns.MdnsAdvertiser;
import com.android.server.connectivity.mdns.MdnsDiscoveryManager;
import com.android.server.connectivity.mdns.MdnsInterfaceSocket;
import com.android.server.connectivity.mdns.MdnsSearchOptions;
import com.android.server.connectivity.mdns.MdnsServiceBrowserListener;
import com.android.server.connectivity.mdns.MdnsServiceInfo;
import com.android.server.connectivity.mdns.MdnsSocketProvider;
import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -101,6 +116,7 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -145,6 +161,11 @@ public class NsdServiceTest {
@Mock MdnsDiscoveryManager mDiscoveryManager;
@Mock MdnsAdvertiser mAdvertiser;
@Mock MdnsSocketProvider mSocketProvider;
@Mock WifiManager mWifiManager;
@Mock WifiManager.MulticastLock mMulticastLock;
@Mock ActivityManager mActivityManager;
SocketRequestMonitor mSocketRequestMonitor;
OnUidImportanceListener mUidImportanceListener;
HandlerThread mThread;
TestHandler mHandler;
NsdService mService;
@@ -167,9 +188,13 @@ public class NsdServiceTest {
mHandler = new TestHandler(mThread.getLooper());
when(mContext.getContentResolver()).thenReturn(mResolver);
mockService(mContext, MDnsManager.class, MDnsManager.MDNS_SERVICE, mMockMDnsM);
mockService(mContext, WifiManager.class, Context.WIFI_SERVICE, mWifiManager);
mockService(mContext, ActivityManager.class, Context.ACTIVITY_SERVICE, mActivityManager);
if (mContext.getSystemService(MDnsManager.class) == null) {
// Test is using mockito-extended
doCallRealMethod().when(mContext).getSystemService(MDnsManager.class);
doCallRealMethod().when(mContext).getSystemService(WifiManager.class);
doCallRealMethod().when(mContext).getSystemService(ActivityManager.class);
}
doReturn(true).when(mMockMDnsM).registerService(
anyInt(), anyString(), anyString(), anyInt(), any(), anyInt());
@@ -180,9 +205,21 @@ public class NsdServiceTest {
doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class));
doReturn(mDiscoveryManager).when(mDeps)
.makeMdnsDiscoveryManager(any(), any(), any());
doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any());
doReturn(mMulticastLock).when(mWifiManager).createMulticastLock(any());
doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any(), any());
doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt(
eq(NsdService.MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF), anyInt());
doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any());
mService = makeService();
final ArgumentCaptor<SocketRequestMonitor> cbMonitorCaptor =
ArgumentCaptor.forClass(SocketRequestMonitor.class);
verify(mDeps).makeMdnsSocketProvider(any(), any(), any(), cbMonitorCaptor.capture());
mSocketRequestMonitor = cbMonitorCaptor.getValue();
final ArgumentCaptor<OnUidImportanceListener> uidListenerCaptor =
ArgumentCaptor.forClass(OnUidImportanceListener.class);
verify(mActivityManager).addOnUidImportanceListener(uidListenerCaptor.capture(), anyInt());
mUidImportanceListener = uidListenerCaptor.getValue();
}
@After
@@ -738,8 +775,8 @@ public class NsdServiceTest {
public void testRegisterAndUnregisterServiceInfoCallback() {
final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
NsdManager.ServiceInfoCallback.class);
final ServiceInfoCallback serviceInfoCallback = mock(
ServiceInfoCallback.class);
final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
final Network network = new Network(999);
request.setNetwork(network);
@@ -813,8 +850,8 @@ public class NsdServiceTest {
final NsdManager client = connectClient(mService);
final String invalidServiceType = "a_service";
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, invalidServiceType);
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
NsdManager.ServiceInfoCallback.class);
final ServiceInfoCallback serviceInfoCallback = mock(
ServiceInfoCallback.class);
client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
waitForIdle();
@@ -826,8 +863,8 @@ public class NsdServiceTest {
@Test
public void testUnregisterNotRegisteredCallback() {
final NsdManager client = connectClient(mService);
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
NsdManager.ServiceInfoCallback.class);
final ServiceInfoCallback serviceInfoCallback = mock(
ServiceInfoCallback.class);
assertThrows(IllegalArgumentException.class, () ->
client.unregisterServiceInfoCallback(serviceInfoCallback));
@@ -1336,6 +1373,194 @@ public class NsdServiceTest {
verify(mDiscoveryManager, times(2)).registerListener(anyString(), any(), any());
}
@Test
@EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testTakeMulticastLockOnBehalfOfClient_ForWifiNetworksOnly() {
// Test on one client in the foreground
mUidImportanceListener.onUidImportance(123, IMPORTANCE_FOREGROUND);
doReturn(123).when(mDeps).getCallingUid();
final NsdManager client = connectClient(mService);
final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
regInfo.setPort(12345);
// File a request for all networks
regInfo.setNetwork(null);
final RegistrationListener regListener = mock(RegistrationListener.class);
client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
verify(mSocketProvider).startMonitoringSockets();
verify(mAdvertiser).addService(anyInt(), any(), any());
final Network wifiNetwork1 = new Network(123);
final Network wifiNetwork2 = new Network(124);
final Network ethernetNetwork = new Network(125);
final MdnsInterfaceSocket wifiNetworkSocket1 = mock(MdnsInterfaceSocket.class);
final MdnsInterfaceSocket wifiNetworkSocket2 = mock(MdnsInterfaceSocket.class);
final MdnsInterfaceSocket ethernetNetworkSocket = mock(MdnsInterfaceSocket.class);
// Nothing happens for networks with no transports, no Wi-Fi transport, or VPN transport
mHandler.post(() -> {
mSocketRequestMonitor.onSocketRequestFulfilled(
new Network(125), mock(MdnsInterfaceSocket.class), new int[0]);
mSocketRequestMonitor.onSocketRequestFulfilled(
ethernetNetwork, ethernetNetworkSocket,
new int[] { TRANSPORT_ETHERNET });
mSocketRequestMonitor.onSocketRequestFulfilled(
new Network(127), mock(MdnsInterfaceSocket.class),
new int[] { TRANSPORT_WIFI, TRANSPORT_VPN });
});
waitForIdle();
verify(mWifiManager, never()).createMulticastLock(any());
// First Wi-Fi network
mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
wifiNetwork1, wifiNetworkSocket1, new int[] { TRANSPORT_WIFI }));
waitForIdle();
verify(mWifiManager).createMulticastLock(any());
verify(mMulticastLock).acquire();
// Second Wi-Fi network
mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
wifiNetwork2, wifiNetworkSocket2, new int[] { TRANSPORT_WIFI }));
waitForIdle();
verifyNoMoreInteractions(mMulticastLock);
// One Wi-Fi network becomes unused, nothing happens
mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
wifiNetwork1, wifiNetworkSocket1));
waitForIdle();
verifyNoMoreInteractions(mMulticastLock);
// Ethernet network becomes unused, still nothing
mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
ethernetNetwork, ethernetNetworkSocket));
waitForIdle();
verifyNoMoreInteractions(mMulticastLock);
// The second Wi-Fi network becomes unused, the lock is released
mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed(
wifiNetwork2, wifiNetworkSocket2));
waitForIdle();
verify(mMulticastLock).release();
}
@Test
@EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND)
public void testTakeMulticastLockOnBehalfOfClient_ForForegroundAppsOnly() {
final int uid1 = 12;
final int uid2 = 34;
final int uid3 = 56;
final int uid4 = 78;
final InOrder lockOrder = inOrder(mMulticastLock);
// Connect one client without any foreground info
doReturn(uid1).when(mDeps).getCallingUid();
final NsdManager client1 = connectClient(mService);
// Connect client2 as visible, but not foreground
mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_VISIBLE);
waitForIdle();
doReturn(uid2).when(mDeps).getCallingUid();
final NsdManager client2 = connectClient(mService);
// Connect client3, client4 as foreground
mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_FOREGROUND);
waitForIdle();
doReturn(uid3).when(mDeps).getCallingUid();
final NsdManager client3 = connectClient(mService);
mUidImportanceListener.onUidImportance(uid4, IMPORTANCE_FOREGROUND);
waitForIdle();
doReturn(uid4).when(mDeps).getCallingUid();
final NsdManager client4 = connectClient(mService);
// First client advertises on any network
final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123")));
regInfo.setPort(12345);
regInfo.setNetwork(null);
final RegistrationListener regListener = mock(RegistrationListener.class);
client1.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener);
waitForIdle();
final MdnsInterfaceSocket wifiSocket = mock(MdnsInterfaceSocket.class);
final Network wifiNetwork = new Network(123);
final MdnsInterfaceSocket ethSocket = mock(MdnsInterfaceSocket.class);
final Network ethNetwork = new Network(234);
mHandler.post(() -> {
mSocketRequestMonitor.onSocketRequestFulfilled(
wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI });
mSocketRequestMonitor.onSocketRequestFulfilled(
ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET });
});
waitForIdle();
// No multicast lock since client1 has no foreground info
lockOrder.verifyNoMoreInteractions();
// Second client discovers specifically on the Wi-Fi network
final DiscoveryListener discListener = mock(DiscoveryListener.class);
client2.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, wifiNetwork,
Runnable::run, discListener);
waitForIdle();
mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI }));
waitForIdle();
// No multicast lock since client2 is not visible enough
lockOrder.verifyNoMoreInteractions();
// Third client registers a callback on all networks
final NsdServiceInfo cbInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
cbInfo.setNetwork(null);
final ServiceInfoCallback infoCb = mock(ServiceInfoCallback.class);
client3.registerServiceInfoCallback(cbInfo, Runnable::run, infoCb);
waitForIdle();
mHandler.post(() -> {
mSocketRequestMonitor.onSocketRequestFulfilled(
wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI });
mSocketRequestMonitor.onSocketRequestFulfilled(
ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET });
});
waitForIdle();
// Multicast lock is taken for third client
lockOrder.verify(mMulticastLock).acquire();
// Client3 goes to the background
mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_CACHED);
waitForIdle();
lockOrder.verify(mMulticastLock).release();
// client4 resolves on a different network
final ResolveListener resolveListener = mock(ResolveListener.class);
final NsdServiceInfo resolveInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
resolveInfo.setNetwork(ethNetwork);
client4.resolveService(resolveInfo, Runnable::run, resolveListener);
waitForIdle();
mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled(
ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET }));
waitForIdle();
// client4 is foreground, but not Wi-Fi
lockOrder.verifyNoMoreInteractions();
// Second client becomes foreground
mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_FOREGROUND);
waitForIdle();
lockOrder.verify(mMulticastLock).acquire();
// Second client is lost
mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_GONE);
waitForIdle();
lockOrder.verify(mMulticastLock).release();
}
private void waitForIdle() {
HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
}

View File

@@ -32,10 +32,12 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -70,6 +72,7 @@ import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.net.module.util.netlink.StructIfaddrMsg;
import com.android.net.module.util.netlink.StructNlMsgHdr;
import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies;
import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor;
import com.android.server.connectivity.mdns.internal.SocketNetlinkMonitor;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -79,6 +82,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -114,6 +118,7 @@ public class MdnsSocketProviderTest {
@Mock private NetworkInterfaceWrapper mTestNetworkIfaceWrapper;
@Mock private NetworkInterfaceWrapper mLocalOnlyIfaceWrapper;
@Mock private NetworkInterfaceWrapper mTetheredIfaceWrapper;
@Mock private SocketRequestMonitor mSocketRequestMonitor;
private Handler mHandler;
private MdnsSocketProvider mSocketProvider;
private NetworkCallback mNetworkCallback;
@@ -165,7 +170,8 @@ public class MdnsSocketProviderTest {
return mTestSocketNetLinkMonitor;
}).when(mDeps).createSocketNetlinkMonitor(any(), any(),
any());
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog);
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog,
mSocketRequestMonitor);
}
private void runOnHandler(Runnable r) {
@@ -319,23 +325,30 @@ public class MdnsSocketProviderTest {
public void testSocketRequestAndUnrequestSocket() {
startMonitoringSockets();
final InOrder cbMonitorOrder = inOrder(mSocketRequestMonitor);
final TestSocketCallback testCallback1 = new TestSocketCallback();
runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
testCallback1.expectedNoCallback();
postNetworkAvailable(TRANSPORT_WIFI);
testCallback1.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketRequestFulfilled(eq(TEST_NETWORK),
any(), eq(new int[] { TRANSPORT_WIFI }));
final TestSocketCallback testCallback2 = new TestSocketCallback();
runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
testCallback1.expectedNoCallback();
testCallback2.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketRequestFulfilled(eq(TEST_NETWORK),
any(), eq(new int[] { TRANSPORT_WIFI }));
final TestSocketCallback testCallback3 = new TestSocketCallback();
runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketRequestFulfilled(eq(TEST_NETWORK),
any(), eq(new int[] { TRANSPORT_WIFI }));
runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
List.of(LOCAL_ONLY_IFACE_NAME)));
@@ -343,6 +356,8 @@ public class MdnsSocketProviderTest {
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketRequestFulfilled(eq(null),
any(), eq(new int[0]));
runOnHandler(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
List.of(TETHERED_IFACE_NAME)));
@@ -350,6 +365,8 @@ public class MdnsSocketProviderTest {
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketRequestFulfilled(eq(null),
any(), eq(new int[0]));
runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback1));
testCallback1.expectedNoCallback();
@@ -360,17 +377,22 @@ public class MdnsSocketProviderTest {
testCallback1.expectedNoCallback();
testCallback2.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
testCallback3.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketDestroyed(eq(TEST_NETWORK), any());
runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketDestroyed(eq(null), any());
runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback3));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
// There was still a tethered interface, but no callback should be sent once unregistered
testCallback3.expectedNoCallback();
// However the socket is getting destroyed, so the callback monitor is notified
cbMonitorOrder.verify(mSocketRequestMonitor).onSocketDestroyed(eq(null), any());
}
private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(