• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.net.Network;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 import com.android.server.connectivity.mdns.util.MdnsLogger;
26 
27 import java.io.IOException;
28 import java.net.Inet4Address;
29 import java.net.Inet6Address;
30 import java.net.InterfaceAddress;
31 import java.net.NetworkInterface;
32 import java.net.SocketException;
33 import java.util.ArrayList;
34 import java.util.Enumeration;
35 import java.util.List;
36 
37 /**
38  * This class is used by the {@link MdnsSocket} to monitor the list of {@link NetworkInterface}
39  * instances that are currently available for multi-cast messaging.
40  */
41 public class MulticastNetworkInterfaceProvider {
42 
43     private static final String TAG = "MdnsNIProvider";
44     private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
45     private static final boolean PREFER_IPV6 = MdnsConfigs.preferIpv6();
46 
47     private final List<NetworkInterfaceWrapper> multicastNetworkInterfaces = new ArrayList<>();
48     // Only modifiable from tests.
49     @VisibleForTesting
50     ConnectivityMonitor connectivityMonitor;
51     private volatile boolean connectivityChanged = true;
52 
53     @SuppressWarnings("nullness:methodref.receiver.bound")
MulticastNetworkInterfaceProvider(@onNull Context context)54     public MulticastNetworkInterfaceProvider(@NonNull Context context) {
55         // IMPORT CHANGED
56         this.connectivityMonitor = new ConnectivityMonitorWithConnectivityManager(
57                 context, this::onConnectivityChanged);
58     }
59 
onConnectivityChanged()60     private synchronized void onConnectivityChanged() {
61         connectivityChanged = true;
62     }
63 
64     /**
65      * Starts monitoring changes of connectivity of this device, which may indicate that the list of
66      * network interfaces available for multi-cast messaging has changed.
67      */
startWatchingConnectivityChanges()68     public void startWatchingConnectivityChanges() {
69         connectivityMonitor.startWatchingConnectivityChanges();
70     }
71 
72     /** Stops monitoring changes of connectivity. */
stopWatchingConnectivityChanges()73     public void stopWatchingConnectivityChanges() {
74         connectivityMonitor.stopWatchingConnectivityChanges();
75     }
76 
77     /**
78      * Returns the list of {@link NetworkInterfaceWrapper} instances available for multi-cast
79      * messaging.
80      */
getMulticastNetworkInterfaces()81     public synchronized List<NetworkInterfaceWrapper> getMulticastNetworkInterfaces() {
82         if (connectivityChanged) {
83             connectivityChanged = false;
84             updateMulticastNetworkInterfaces();
85             if (multicastNetworkInterfaces.isEmpty()) {
86                 LOGGER.log("No network interface available for mDNS scanning.");
87             }
88         }
89         return new ArrayList<>(multicastNetworkInterfaces);
90     }
91 
updateMulticastNetworkInterfaces()92     private void updateMulticastNetworkInterfaces() {
93         multicastNetworkInterfaces.clear();
94         List<NetworkInterfaceWrapper> networkInterfaceWrappers = getNetworkInterfaces();
95         for (NetworkInterfaceWrapper interfaceWrapper : networkInterfaceWrappers) {
96             if (canScanOnInterface(interfaceWrapper)) {
97                 multicastNetworkInterfaces.add(interfaceWrapper);
98             }
99         }
100     }
101 
isOnIpV6OnlyNetwork(List<NetworkInterfaceWrapper> networkInterfaces)102     public boolean isOnIpV6OnlyNetwork(List<NetworkInterfaceWrapper> networkInterfaces) {
103         if (networkInterfaces.isEmpty()) {
104             return false;
105         }
106 
107         // TODO(b/79866499): Remove this when the bug is resolved.
108         if (PREFER_IPV6) {
109             return true;
110         }
111         boolean hasAtleastOneIPv6Address = false;
112         for (NetworkInterfaceWrapper interfaceWrapper : networkInterfaces) {
113             for (InterfaceAddress ifAddr : interfaceWrapper.getInterfaceAddresses()) {
114                 if (!(ifAddr.getAddress() instanceof Inet6Address)) {
115                     return false;
116                 } else {
117                     hasAtleastOneIPv6Address = true;
118                 }
119             }
120         }
121         return hasAtleastOneIPv6Address;
122     }
123 
124     @VisibleForTesting
getNetworkInterfaces()125     List<NetworkInterfaceWrapper> getNetworkInterfaces() {
126         List<NetworkInterfaceWrapper> networkInterfaceWrappers = new ArrayList<>();
127         try {
128             Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
129             if (interfaces != null) {
130                 while (interfaces.hasMoreElements()) {
131                     networkInterfaceWrappers.add(
132                             new NetworkInterfaceWrapper(interfaces.nextElement()));
133                 }
134             }
135         } catch (SocketException e) {
136             LOGGER.e("Failed to get network interfaces.", e);
137         } catch (NullPointerException e) {
138             // Android R has a bug that could lead to a NPE. See b/159277702.
139             LOGGER.e("Failed to call getNetworkInterfaces API", e);
140         }
141 
142         return networkInterfaceWrappers;
143     }
144 
145     @Nullable
getAvailableNetwork()146     public Network getAvailableNetwork() {
147         return connectivityMonitor.getAvailableNetwork();
148     }
149 
150     /*** Check whether given network interface can support mdns */
canScanOnInterface(@ullable NetworkInterfaceWrapper networkInterface)151     private static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface) {
152         try {
153             if ((networkInterface == null)
154                     || networkInterface.isLoopback()
155                     || networkInterface.isPointToPoint()
156                     || networkInterface.isVirtual()
157                     || !networkInterface.isUp()
158                     || !networkInterface.supportsMulticast()) {
159                 return false;
160             }
161             return hasInet4Address(networkInterface) || hasInet6Address(networkInterface);
162         } catch (IOException e) {
163             LOGGER.e(String.format("Failed to check interface %s.",
164                     networkInterface.getNetworkInterface().getDisplayName()), e);
165         }
166 
167         return false;
168     }
169 
hasInet4Address(@onNull NetworkInterfaceWrapper networkInterface)170     private static boolean hasInet4Address(@NonNull NetworkInterfaceWrapper networkInterface) {
171         for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
172             if (ifAddr.getAddress() instanceof Inet4Address) {
173                 return true;
174             }
175         }
176         return false;
177     }
178 
hasInet6Address(@onNull NetworkInterfaceWrapper networkInterface)179     private static boolean hasInet6Address(@NonNull NetworkInterfaceWrapper networkInterface) {
180         for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) {
181             if (ifAddr.getAddress() instanceof Inet6Address) {
182                 return true;
183             }
184         }
185         return false;
186     }
187 }