• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.connectivity.mdns;
18 
19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
20 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
21 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
22 
23 import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
24 import static com.android.server.connectivity.mdns.util.MdnsUtils.isNetworkMatched;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.net.ConnectivityManager;
33 import android.net.ConnectivityManager.NetworkCallback;
34 import android.net.LinkAddress;
35 import android.net.LinkProperties;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.NetworkRequest;
39 import android.net.TetheringManager;
40 import android.net.TetheringManager.TetheringEventCallback;
41 import android.net.wifi.p2p.WifiP2pGroup;
42 import android.net.wifi.p2p.WifiP2pInfo;
43 import android.net.wifi.p2p.WifiP2pManager;
44 import android.os.Handler;
45 import android.os.Looper;
46 import android.util.ArrayMap;
47 import android.util.Log;
48 import android.util.SparseArray;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.net.module.util.CollectionUtils;
52 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
53 import com.android.net.module.util.SharedLog;
54 
55 import java.io.IOException;
56 import java.net.NetworkInterface;
57 import java.net.SocketException;
58 import java.util.ArrayList;
59 import java.util.List;
60 import java.util.Objects;
61 
62 /**
63  * The {@link MdnsSocketProvider} manages the multiple sockets for mDns.
64  *
65  * <p>This class is not thread safe, it is intended to be used only from the looper thread.
66  * However, the constructor is an exception, as it is called on another thread;
67  * therefore for thread safety all members of this class MUST either be final or initialized
68  * to their default value (0, false or null).
69  *
70  */
71 public class MdnsSocketProvider {
72     private static final String TAG = MdnsSocketProvider.class.getSimpleName();
73     private static final boolean DBG = MdnsDiscoveryManager.DBG;
74     // This buffer size matches what MdnsSocketClient uses currently.
75     // But 1440 should generally be enough because of standard Ethernet.
76     // Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
77     private static final int READ_BUFFER_SIZE = 2048;
78     private static final int IFACE_IDX_NOT_EXIST = -1;
79     @NonNull private final Context mContext;
80     @NonNull private final Looper mLooper;
81     @NonNull private final Handler mHandler;
82     @NonNull private final Dependencies mDependencies;
83     @NonNull private final NetworkCallback mNetworkCallback;
84     @NonNull private final TetheringEventCallback mTetheringEventCallback;
85     @NonNull private final AbstractSocketNetlink mSocketNetlinkMonitor;
86     @NonNull private final SharedLog mSharedLog;
87     private final ArrayMap<Network, SocketInfo> mNetworkSockets = new ArrayMap<>();
88     private final ArrayMap<String, SocketInfo> mTetherInterfaceSockets = new ArrayMap<>();
89     private final ArrayMap<Network, LinkProperties> mActiveNetworksLinkProperties =
90             new ArrayMap<>();
91     private final ArrayMap<Network, int[]> mActiveNetworksTransports = new ArrayMap<>();
92     private final ArrayMap<SocketCallback, Network> mCallbacksToRequestedNetworks =
93             new ArrayMap<>();
94     private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
95     private final List<String> mTetheredInterfaces = new ArrayList<>();
96     // mIfaceIdxToLinkProperties should not be cleared in maybeStopMonitoringSockets() because
97     // the netlink monitor is never stop and the old states must be kept.
98     private final SparseArray<LinkProperties> mIfaceIdxToLinkProperties = new SparseArray<>();
99     private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
100     private boolean mMonitoringSockets = false;
101     private boolean mRequestStop = false;
102     private String mWifiP2pTetherInterface = null;
103 
104     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
105         @Override
106         public void onReceive(Context context, Intent intent) {
107             final String newP2pIface = getWifiP2pInterface(intent);
108 
109             if (!mMonitoringSockets || !hasAllNetworksRequest()) {
110                 mWifiP2pTetherInterface = newP2pIface;
111                 return;
112             }
113 
114             // If already serving from the correct interface, nothing to do.
115             if (Objects.equals(mWifiP2pTetherInterface, newP2pIface)) return;
116 
117             if (mWifiP2pTetherInterface != null) {
118                 if (newP2pIface != null) {
119                     Log.wtf(TAG, "Wifi p2p interface is changed from " + mWifiP2pTetherInterface
120                             + " to " + newP2pIface + " without null broadcast");
121                 }
122                 // Remove the socket.
123                 removeTetherInterfaceSocket(mWifiP2pTetherInterface);
124             }
125 
126             // Update mWifiP2pTetherInterface
127             mWifiP2pTetherInterface = newP2pIface;
128 
129             // Check whether the socket for wifi p2p interface is created or not.
130             final boolean socketAlreadyExists = mTetherInterfaceSockets.get(newP2pIface) != null;
131             if (newP2pIface != null && !socketAlreadyExists) {
132                 // Create a socket for wifi p2p interface.
133                 final int ifaceIndex =
134                         mDependencies.getNetworkInterfaceIndexByName(newP2pIface);
135                 createSocket(LOCAL_NET, createLPForTetheredInterface(newP2pIface, ifaceIndex));
136             }
137         }
138     };
139 
140     @Nullable
getWifiP2pInterface(final Intent intent)141     private static String getWifiP2pInterface(final Intent intent) {
142         final WifiP2pGroup group =
143                 intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
144         final WifiP2pInfo p2pInfo =
145                 intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
146         if (group == null || p2pInfo == null) {
147             return null;
148         }
149 
150         if (!p2pInfo.groupFormed) {
151             return null;
152         } else {
153             return group.getInterface();
154         }
155     }
156 
MdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull SharedLog sharedLog)157     public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
158             @NonNull SharedLog sharedLog) {
159         this(context, looper, new Dependencies(), sharedLog);
160     }
161 
MdnsSocketProvider(@onNull Context context, @NonNull Looper looper, @NonNull Dependencies deps, @NonNull SharedLog sharedLog)162     MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
163             @NonNull Dependencies deps, @NonNull SharedLog sharedLog) {
164         mContext = context;
165         mLooper = looper;
166         mHandler = new Handler(looper);
167         mDependencies = deps;
168         mSharedLog = sharedLog;
169         mNetworkCallback = new NetworkCallback() {
170             @Override
171             public void onLost(Network network) {
172                 mActiveNetworksLinkProperties.remove(network);
173                 mActiveNetworksTransports.remove(network);
174                 removeNetworkSocket(network);
175             }
176 
177             @Override
178             public void onCapabilitiesChanged(@NonNull Network network,
179                     @NonNull NetworkCapabilities networkCapabilities) {
180                 mActiveNetworksTransports.put(network, networkCapabilities.getTransportTypes());
181             }
182 
183             @Override
184             public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
185                 handleLinkPropertiesChanged(network, lp);
186             }
187         };
188         mTetheringEventCallback = new TetheringEventCallback() {
189             @Override
190             public void onLocalOnlyInterfacesChanged(@NonNull List<String> interfaces) {
191                 handleTetherInterfacesChanged(mLocalOnlyInterfaces, interfaces);
192             }
193 
194             @Override
195             public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {
196                 handleTetherInterfacesChanged(mTetheredInterfaces, interfaces);
197             }
198         };
199 
200         mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler,
201                 mSharedLog.forSubComponent("NetlinkMonitor"), new NetLinkMessageProcessor());
202 
203         // Register a intent receiver to listen wifi p2p interface changes.
204         // Note: The wifi p2p interface change is only notified via
205         // TetheringEventCallback#onLocalOnlyInterfacesChanged if the device is the wifi p2p group
206         // owner. In this case, MdnsSocketProvider will receive duplicate interface changes and must
207         // ignore the later notification because the socket has already been created. There is only
208         // one notification from the wifi p2p connection change intent if the device is not the wifi
209         // p2p group owner.
210         final IntentFilter intentFilter =
211                 new IntentFilter(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
212         mContext.registerReceiver(
213                 mIntentReceiver, intentFilter, null /* broadcastPermission */, mHandler);
214     }
215 
216     /**
217      * Dependencies of MdnsSocketProvider, for injection in tests.
218      */
219     @VisibleForTesting
220     public static class Dependencies {
221         /*** Get network interface by given interface name */
getNetworkInterfaceByName(@onNull String interfaceName)222         public NetworkInterfaceWrapper getNetworkInterfaceByName(@NonNull String interfaceName)
223                 throws SocketException {
224             final NetworkInterface ni = NetworkInterface.getByName(interfaceName);
225             return ni == null ? null : new NetworkInterfaceWrapper(ni);
226         }
227 
228         /*** Create a MdnsInterfaceSocket */
createMdnsInterfaceSocket( @onNull NetworkInterface networkInterface, int port, @NonNull Looper looper, @NonNull byte[] packetReadBuffer)229         public MdnsInterfaceSocket createMdnsInterfaceSocket(
230                 @NonNull NetworkInterface networkInterface, int port, @NonNull Looper looper,
231                 @NonNull byte[] packetReadBuffer) throws IOException {
232             return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
233         }
234 
235         /*** Get network interface by given interface name */
getNetworkInterfaceIndexByName(@onNull final String ifaceName)236         public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName) {
237             final NetworkInterface iface;
238             try {
239                 iface = NetworkInterface.getByName(ifaceName);
240             } catch (SocketException e) {
241                 Log.e(TAG, "Error querying interface", e);
242                 return IFACE_IDX_NOT_EXIST;
243             }
244             if (iface == null) {
245                 Log.e(TAG, "Interface not found: " + ifaceName);
246                 return IFACE_IDX_NOT_EXIST;
247             }
248             return iface.getIndex();
249         }
250         /*** Creates a SocketNetlinkMonitor */
createSocketNetlinkMonitor(@onNull final Handler handler, @NonNull final SharedLog log, @NonNull final NetLinkMonitorCallBack cb)251         public AbstractSocketNetlink createSocketNetlinkMonitor(@NonNull final Handler handler,
252                 @NonNull final SharedLog log,
253                 @NonNull final NetLinkMonitorCallBack cb) {
254             return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
255         }
256     }
257     /**
258      * The callback interface for the netlink monitor messages.
259      */
260     public interface NetLinkMonitorCallBack {
261         /**
262          * Handles the interface address add or update.
263          */
addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull LinkAddress newAddress)264         void addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull LinkAddress newAddress);
265 
266 
267         /**
268          * Handles the interface address delete.
269          */
deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress)270         void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress);
271     }
272     private class NetLinkMessageProcessor implements NetLinkMonitorCallBack {
273 
274         @Override
addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull final LinkAddress newAddress)275         public void addOrUpdateInterfaceAddress(int ifaceIdx,
276                 @NonNull final LinkAddress newAddress) {
277 
278             LinkProperties linkProperties;
279             linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
280             if (linkProperties == null) {
281                 linkProperties = new LinkProperties();
282                 mIfaceIdxToLinkProperties.put(ifaceIdx, linkProperties);
283             }
284             boolean updated = linkProperties.addLinkAddress(newAddress);
285 
286             if (!updated) {
287                 return;
288             }
289             maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
290         }
291 
292         @Override
deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress)293         public void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress) {
294             LinkProperties linkProperties;
295             boolean updated = false;
296             linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
297             if (linkProperties != null) {
298                 updated = linkProperties.removeLinkAddress(deleteAddress);
299                 if (linkProperties.getLinkAddresses().isEmpty()) {
300                     mIfaceIdxToLinkProperties.remove(ifaceIdx);
301                 }
302             }
303 
304             if (linkProperties == null || !updated) {
305                 return;
306             }
307             maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
308 
309         }
310     }
311     /*** Data class for storing socket related info  */
312     private static class SocketInfo {
313         final MdnsInterfaceSocket mSocket;
314         final List<LinkAddress> mAddresses;
315 
SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses)316         SocketInfo(MdnsInterfaceSocket socket, List<LinkAddress> addresses) {
317             mSocket = socket;
318             mAddresses = new ArrayList<>(addresses);
319         }
320     }
321 
322     /*** Start monitoring sockets by listening callbacks for sockets creation or removal */
startMonitoringSockets()323     public void startMonitoringSockets() {
324         ensureRunningOnHandlerThread(mHandler);
325         mRequestStop = false; // Reset stop request flag.
326         if (mMonitoringSockets) {
327             Log.d(TAG, "Already monitoring sockets.");
328             return;
329         }
330         mSharedLog.i("Start monitoring sockets.");
331         mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
332                 new NetworkRequest.Builder().clearCapabilities().build(),
333                 mNetworkCallback, mHandler);
334 
335         final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
336         tetheringManager.registerTetheringEventCallback(mHandler::post, mTetheringEventCallback);
337 
338         if (mSocketNetlinkMonitor.isSupported()) {
339             mHandler.post(mSocketNetlinkMonitor::startMonitoring);
340         }
341         mMonitoringSockets = true;
342     }
343     /**
344      * Start netlink monitor.
345      */
startNetLinkMonitor()346     public void startNetLinkMonitor() {
347         ensureRunningOnHandlerThread(mHandler);
348         if (mSocketNetlinkMonitor.isSupported()) {
349             mSocketNetlinkMonitor.startMonitoring();
350         }
351     }
352 
maybeStopMonitoringSockets()353     private void maybeStopMonitoringSockets() {
354         if (!mMonitoringSockets) return; // Already unregistered.
355         if (!mRequestStop) return; // No stop request.
356 
357         // Only unregister the network callback if there is no socket request.
358         if (mCallbacksToRequestedNetworks.isEmpty()) {
359             mSharedLog.i("Stop monitoring sockets.");
360             mContext.getSystemService(ConnectivityManager.class)
361                     .unregisterNetworkCallback(mNetworkCallback);
362 
363             final TetheringManager tetheringManager = mContext.getSystemService(
364                     TetheringManager.class);
365             tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
366             // Clear all saved status.
367             mActiveNetworksLinkProperties.clear();
368             mNetworkSockets.clear();
369             mTetherInterfaceSockets.clear();
370             mLocalOnlyInterfaces.clear();
371             mTetheredInterfaces.clear();
372             mMonitoringSockets = false;
373         }
374         // The netlink monitor is not stopped here because the MdnsSocketProvider need to listen
375         // to all the netlink updates when the system is up and running.
376     }
377 
378     /*** Request to stop monitoring sockets and unregister callbacks */
requestStopWhenInactive()379     public void requestStopWhenInactive() {
380         ensureRunningOnHandlerThread(mHandler);
381         if (!mMonitoringSockets) {
382             Log.d(TAG, "Monitoring sockets hasn't been started.");
383             return;
384         }
385         mRequestStop = true;
386         maybeStopMonitoringSockets();
387     }
388 
matchRequestedNetwork(Network network)389     private boolean matchRequestedNetwork(Network network) {
390         return hasAllNetworksRequest()
391                 || mCallbacksToRequestedNetworks.containsValue(network);
392     }
393 
hasAllNetworksRequest()394     private boolean hasAllNetworksRequest() {
395         return mCallbacksToRequestedNetworks.containsValue(null);
396     }
397 
handleLinkPropertiesChanged(Network network, LinkProperties lp)398     private void handleLinkPropertiesChanged(Network network, LinkProperties lp) {
399         mActiveNetworksLinkProperties.put(network, lp);
400         if (!matchRequestedNetwork(network)) {
401             if (DBG) {
402                 Log.d(TAG, "Ignore LinkProperties change. There is no request for the"
403                         + " Network:" + network);
404             }
405             return;
406         }
407 
408         final NetworkAsKey networkKey = new NetworkAsKey(network);
409         final SocketInfo socketInfo = mNetworkSockets.get(network);
410         if (socketInfo == null) {
411             createSocket(networkKey, lp);
412         } else {
413             updateSocketInfoAddress(network, socketInfo, lp.getLinkAddresses());
414         }
415     }
maybeUpdateTetheringSocketAddress(int ifaceIndex, @NonNull final List<LinkAddress> updatedAddresses)416     private void maybeUpdateTetheringSocketAddress(int ifaceIndex,
417             @NonNull final List<LinkAddress> updatedAddresses) {
418         for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
419             String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
420             if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
421                     == ifaceIndex) {
422                 updateSocketInfoAddress(null /* network */,
423                         mTetherInterfaceSockets.valueAt(i), updatedAddresses);
424                 return;
425             }
426         }
427     }
428 
updateSocketInfoAddress(@ullable final Network network, @NonNull final SocketInfo socketInfo, @NonNull final List<LinkAddress> addresses)429     private void updateSocketInfoAddress(@Nullable final Network network,
430             @NonNull final SocketInfo socketInfo,
431             @NonNull final List<LinkAddress> addresses) {
432         // Update the addresses of this socket.
433         socketInfo.mAddresses.clear();
434         socketInfo.mAddresses.addAll(addresses);
435         // Try to join the group again.
436         socketInfo.mSocket.joinGroup(addresses);
437 
438         notifyAddressesChanged(network, socketInfo.mSocket, addresses);
439     }
createLPForTetheredInterface(@onNull final String interfaceName, int ifaceIndex)440     private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
441             int ifaceIndex) {
442         final LinkProperties linkProperties =
443                 new LinkProperties(mIfaceIdxToLinkProperties.get(ifaceIndex));
444         linkProperties.setInterfaceName(interfaceName);
445         return linkProperties;
446     }
447 
handleTetherInterfacesChanged(List<String> current, List<String> updated)448     private void handleTetherInterfacesChanged(List<String> current, List<String> updated) {
449         if (!hasAllNetworksRequest()) {
450             // Currently, the network for tethering can not be requested, so the sockets for
451             // tethering are only created if there is a request for all networks (interfaces).
452             // Therefore, only update the interface list and skip this change if no such request.
453             if (DBG) {
454                 Log.d(TAG, "Ignore tether interfaces change. There is no request for all"
455                         + " networks.");
456             }
457             current.clear();
458             current.addAll(updated);
459             return;
460         }
461 
462         final CompareResult<String> interfaceDiff = new CompareResult<>(
463                 current, updated);
464         for (String name : interfaceDiff.added) {
465             // Check if a socket has been created for the interface
466             final SocketInfo socketInfo = mTetherInterfaceSockets.get(name);
467             if (socketInfo != null) {
468                 if (DBG) {
469                     mSharedLog.i("Socket is existed for interface:" + name);
470                 }
471                 continue;
472             }
473 
474             int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
475             createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
476         }
477         for (String name : interfaceDiff.removed) {
478             removeTetherInterfaceSocket(name);
479         }
480         current.clear();
481         current.addAll(updated);
482     }
483 
createSocket(NetworkKey networkKey, LinkProperties lp)484     private void createSocket(NetworkKey networkKey, LinkProperties lp) {
485         final String interfaceName = lp.getInterfaceName();
486         if (interfaceName == null) {
487             Log.e(TAG, "Can not create socket with null interface name.");
488             return;
489         }
490 
491         try {
492             final NetworkInterfaceWrapper networkInterface =
493                     mDependencies.getNetworkInterfaceByName(interfaceName);
494             // There are no transports for tethered interfaces. Other interfaces should always
495             // have transports since LinkProperties updates are always sent after
496             // NetworkCapabilities updates.
497             final int[] transports;
498             if (networkKey == LOCAL_NET) {
499                 transports = new int[0];
500             } else {
501                 transports = mActiveNetworksTransports.getOrDefault(
502                         ((NetworkAsKey) networkKey).mNetwork, new int[0]);
503             }
504             if (networkInterface == null || !isMdnsCapableInterface(networkInterface, transports)) {
505                 return;
506             }
507 
508             mSharedLog.log("Create socket on net:" + networkKey + ", ifName:" + interfaceName);
509             final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
510                     networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
511                     mPacketReadBuffer);
512             final List<LinkAddress> addresses = lp.getLinkAddresses();
513             if (networkKey == LOCAL_NET) {
514                 mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
515             } else {
516                 mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
517                         new SocketInfo(socket, addresses));
518             }
519             // Try to join IPv4/IPv6 group.
520             socket.joinGroup(addresses);
521 
522             // Notify the listeners which need this socket.
523             if (networkKey == LOCAL_NET) {
524                 notifySocketCreated(null /* network */, socket, addresses);
525             } else {
526                 notifySocketCreated(((NetworkAsKey) networkKey).mNetwork, socket, addresses);
527             }
528         } catch (IOException e) {
529             mSharedLog.e("Create socket failed ifName:" + interfaceName, e);
530         }
531     }
532 
isMdnsCapableInterface( @onNull NetworkInterfaceWrapper iface, @NonNull int[] transports)533     private boolean isMdnsCapableInterface(
534             @NonNull NetworkInterfaceWrapper iface, @NonNull int[] transports) {
535         try {
536             // Never try mDNS on cellular, or on interfaces with incompatible flags
537             if (CollectionUtils.contains(transports, TRANSPORT_CELLULAR)
538                     || iface.isLoopback()
539                     || iface.isPointToPoint()
540                     || iface.isVirtual()
541                     || !iface.isUp()) {
542                 return false;
543             }
544 
545             // Otherwise, always try mDNS on non-VPN Wifi.
546             if (!CollectionUtils.contains(transports, TRANSPORT_VPN)
547                     && CollectionUtils.contains(transports, TRANSPORT_WIFI)) {
548                 return true;
549             }
550 
551             // For other transports, or no transports (tethering downstreams), do mDNS based on the
552             // interface flags. This is not always reliable (for example some Wifi interfaces may
553             // not have the MULTICAST flag even though they can do mDNS, and some cellular
554             // interfaces may have the BROADCAST or MULTICAST flags), so checks are done based on
555             // transports above in priority.
556             return iface.supportsMulticast();
557         } catch (SocketException e) {
558             mSharedLog.e("Error checking interface flags", e);
559             return false;
560         }
561     }
562 
removeNetworkSocket(Network network)563     private void removeNetworkSocket(Network network) {
564         final SocketInfo socketInfo = mNetworkSockets.remove(network);
565         if (socketInfo == null) return;
566 
567         socketInfo.mSocket.destroy();
568         notifyInterfaceDestroyed(network, socketInfo.mSocket);
569         mSharedLog.log("Remove socket on net:" + network);
570     }
571 
removeTetherInterfaceSocket(String interfaceName)572     private void removeTetherInterfaceSocket(String interfaceName) {
573         final SocketInfo socketInfo = mTetherInterfaceSockets.remove(interfaceName);
574         if (socketInfo == null) return;
575         socketInfo.mSocket.destroy();
576         notifyInterfaceDestroyed(null /* network */, socketInfo.mSocket);
577         mSharedLog.log("Remove socket on ifName:" + interfaceName);
578     }
579 
notifySocketCreated(Network network, MdnsInterfaceSocket socket, List<LinkAddress> addresses)580     private void notifySocketCreated(Network network, MdnsInterfaceSocket socket,
581             List<LinkAddress> addresses) {
582         for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
583             final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
584             if (isNetworkMatched(requestedNetwork, network)) {
585                 mCallbacksToRequestedNetworks.keyAt(i).onSocketCreated(network, socket, addresses);
586             }
587         }
588     }
589 
notifyInterfaceDestroyed(Network network, MdnsInterfaceSocket socket)590     private void notifyInterfaceDestroyed(Network network, MdnsInterfaceSocket socket) {
591         for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
592             final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
593             if (isNetworkMatched(requestedNetwork, network)) {
594                 mCallbacksToRequestedNetworks.keyAt(i).onInterfaceDestroyed(network, socket);
595             }
596         }
597     }
598 
notifyAddressesChanged(Network network, MdnsInterfaceSocket socket, List<LinkAddress> addresses)599     private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
600             List<LinkAddress> addresses) {
601         for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
602             final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
603             if (isNetworkMatched(requestedNetwork, network)) {
604                 mCallbacksToRequestedNetworks.keyAt(i)
605                         .onAddressesChanged(network, socket, addresses);
606             }
607         }
608     }
609 
retrieveAndNotifySocketFromNetwork(Network network, SocketCallback cb)610     private void retrieveAndNotifySocketFromNetwork(Network network, SocketCallback cb) {
611         final SocketInfo socketInfo = mNetworkSockets.get(network);
612         if (socketInfo == null) {
613             final LinkProperties lp = mActiveNetworksLinkProperties.get(network);
614             if (lp == null) {
615                 // The requested network is not existed. Maybe wait for LinkProperties change later.
616                 if (DBG) Log.d(TAG, "There is no LinkProperties for this network:" + network);
617                 return;
618             }
619             createSocket(new NetworkAsKey(network), lp);
620         } else {
621             // Notify the socket for requested network.
622             cb.onSocketCreated(network, socketInfo.mSocket, socketInfo.mAddresses);
623         }
624     }
625 
retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb)626     private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
627         final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
628         if (socketInfo == null) {
629             int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
630             createSocket(
631                     LOCAL_NET,
632                     createLPForTetheredInterface(interfaceName, ifaceIndex));
633         } else {
634             // Notify the socket for requested network.
635             cb.onSocketCreated(
636                     null /* network */, socketInfo.mSocket, socketInfo.mAddresses);
637         }
638     }
639 
640     /**
641      * Request a socket for given network.
642      *
643      * @param network the required network for a socket. Null means create sockets on all possible
644      *                networks (interfaces).
645      * @param cb the callback to listen the socket creation.
646      */
requestSocket(@ullable Network network, @NonNull SocketCallback cb)647     public void requestSocket(@Nullable Network network, @NonNull SocketCallback cb) {
648         ensureRunningOnHandlerThread(mHandler);
649         mSharedLog.log("requestSocket for net:" + network);
650         mCallbacksToRequestedNetworks.put(cb, network);
651         if (network == null) {
652             // Does not specify a required network, create sockets for all possible
653             // networks (interfaces).
654             for (int i = 0; i < mActiveNetworksLinkProperties.size(); i++) {
655                 retrieveAndNotifySocketFromNetwork(mActiveNetworksLinkProperties.keyAt(i), cb);
656             }
657 
658             for (String localInterface : mLocalOnlyInterfaces) {
659                 retrieveAndNotifySocketFromInterface(localInterface, cb);
660             }
661 
662             for (String tetheredInterface : mTetheredInterfaces) {
663                 retrieveAndNotifySocketFromInterface(tetheredInterface, cb);
664             }
665 
666             if (mWifiP2pTetherInterface != null
667                     && !mLocalOnlyInterfaces.contains(mWifiP2pTetherInterface)) {
668                 retrieveAndNotifySocketFromInterface(mWifiP2pTetherInterface, cb);
669             }
670         } else {
671             retrieveAndNotifySocketFromNetwork(network, cb);
672         }
673     }
674 
675     /*** Unrequest the socket */
unrequestSocket(@onNull SocketCallback cb)676     public void unrequestSocket(@NonNull SocketCallback cb) {
677         ensureRunningOnHandlerThread(mHandler);
678         mSharedLog.log("unrequestSocket");
679         mCallbacksToRequestedNetworks.remove(cb);
680         if (hasAllNetworksRequest()) {
681             // Still has a request for all networks (interfaces).
682             return;
683         }
684 
685         // Check if remaining requests are matched any of sockets.
686         for (int i = mNetworkSockets.size() - 1; i >= 0; i--) {
687             final Network network = mNetworkSockets.keyAt(i);
688             if (matchRequestedNetwork(network)) continue;
689             final SocketInfo info = mNetworkSockets.removeAt(i);
690             info.mSocket.destroy();
691             mSharedLog.log("Remove socket on net:" + network + " after unrequestSocket");
692         }
693 
694         // Remove all sockets for tethering interface because these sockets do not have associated
695         // networks, and they should invoke by a request for all networks (interfaces). If there is
696         // no such request, the sockets for tethering interface should be removed.
697         for (int i = mTetherInterfaceSockets.size() - 1; i >= 0; i--) {
698             final SocketInfo info = mTetherInterfaceSockets.valueAt(i);
699             info.mSocket.destroy();
700             mSharedLog.log("Remove socket on ifName:" + mTetherInterfaceSockets.keyAt(i)
701                     + " after unrequestSocket");
702         }
703         mTetherInterfaceSockets.clear();
704 
705         // Try to unregister network callback.
706         maybeStopMonitoringSockets();
707     }
708 
709 
710     /*** Callbacks for listening socket changes */
711     public interface SocketCallback {
712         /*** Notify the socket is created */
onSocketCreated(@ullable Network network, @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses)713         default void onSocketCreated(@Nullable Network network, @NonNull MdnsInterfaceSocket socket,
714                 @NonNull List<LinkAddress> addresses) {}
715         /*** Notify the interface is destroyed */
onInterfaceDestroyed(@ullable Network network, @NonNull MdnsInterfaceSocket socket)716         default void onInterfaceDestroyed(@Nullable Network network,
717                 @NonNull MdnsInterfaceSocket socket) {}
718         /*** Notify the addresses is changed on the network */
onAddressesChanged(@ullable Network network, @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses)719         default void onAddressesChanged(@Nullable Network network,
720                 @NonNull MdnsInterfaceSocket socket, @NonNull List<LinkAddress> addresses) {}
721     }
722 
723     private interface NetworkKey {
724     }
725 
726     private static final NetworkKey LOCAL_NET = new NetworkKey() {
727         @Override
728         public String toString() {
729             return "NetworkKey:LOCAL_NET";
730         }
731     };
732 
733     private static class NetworkAsKey implements NetworkKey {
734         private final Network mNetwork;
735 
NetworkAsKey(Network network)736         NetworkAsKey(Network network) {
737             this.mNetwork = network;
738         }
739 
740         @Override
hashCode()741         public int hashCode() {
742             return mNetwork.hashCode();
743         }
744 
745         @Override
equals(@ullable Object other)746         public boolean equals(@Nullable Object other) {
747             if (!(other instanceof NetworkAsKey)) {
748                 return false;
749             }
750             return mNetwork.equals(((NetworkAsKey) other).mNetwork);
751         }
752 
753         @Override
toString()754         public String toString() {
755             return "NetworkAsKey{ network=" + mNetwork + " }";
756         }
757     }
758 }
759