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.net.module.util.SharedLog; 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 final SharedLog sharedLog; 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, @NonNull SharedLog sharedLog)54 public MulticastNetworkInterfaceProvider(@NonNull Context context, 55 @NonNull SharedLog sharedLog) { 56 this.sharedLog = sharedLog; 57 // IMPORT CHANGED 58 this.connectivityMonitor = new ConnectivityMonitorWithConnectivityManager( 59 context, this::onConnectivityChanged, sharedLog); 60 } 61 onConnectivityChanged()62 private synchronized void onConnectivityChanged() { 63 connectivityChanged = true; 64 } 65 66 /** 67 * Starts monitoring changes of connectivity of this device, which may indicate that the list of 68 * network interfaces available for multi-cast messaging has changed. 69 */ startWatchingConnectivityChanges()70 public void startWatchingConnectivityChanges() { 71 connectivityMonitor.startWatchingConnectivityChanges(); 72 } 73 74 /** Stops monitoring changes of connectivity. */ stopWatchingConnectivityChanges()75 public void stopWatchingConnectivityChanges() { 76 connectivityMonitor.stopWatchingConnectivityChanges(); 77 } 78 79 /** 80 * Returns the list of {@link NetworkInterfaceWrapper} instances available for multi-cast 81 * messaging. 82 */ getMulticastNetworkInterfaces()83 public synchronized List<NetworkInterfaceWrapper> getMulticastNetworkInterfaces() { 84 if (connectivityChanged) { 85 connectivityChanged = false; 86 updateMulticastNetworkInterfaces(); 87 if (multicastNetworkInterfaces.isEmpty()) { 88 sharedLog.log("No network interface available for mDNS scanning."); 89 } 90 } 91 return new ArrayList<>(multicastNetworkInterfaces); 92 } 93 updateMulticastNetworkInterfaces()94 private void updateMulticastNetworkInterfaces() { 95 multicastNetworkInterfaces.clear(); 96 List<NetworkInterfaceWrapper> networkInterfaceWrappers = getNetworkInterfaces(); 97 for (NetworkInterfaceWrapper interfaceWrapper : networkInterfaceWrappers) { 98 if (canScanOnInterface(interfaceWrapper, sharedLog)) { 99 multicastNetworkInterfaces.add(interfaceWrapper); 100 } 101 } 102 } 103 isOnIpV6OnlyNetwork(List<NetworkInterfaceWrapper> networkInterfaces)104 public boolean isOnIpV6OnlyNetwork(List<NetworkInterfaceWrapper> networkInterfaces) { 105 if (networkInterfaces.isEmpty()) { 106 return false; 107 } 108 109 // TODO(b/79866499): Remove this when the bug is resolved. 110 if (PREFER_IPV6) { 111 return true; 112 } 113 boolean hasAtleastOneIPv6Address = false; 114 for (NetworkInterfaceWrapper interfaceWrapper : networkInterfaces) { 115 for (InterfaceAddress ifAddr : interfaceWrapper.getInterfaceAddresses()) { 116 if (!(ifAddr.getAddress() instanceof Inet6Address)) { 117 return false; 118 } else { 119 hasAtleastOneIPv6Address = true; 120 } 121 } 122 } 123 return hasAtleastOneIPv6Address; 124 } 125 126 @VisibleForTesting getNetworkInterfaces()127 List<NetworkInterfaceWrapper> getNetworkInterfaces() { 128 List<NetworkInterfaceWrapper> networkInterfaceWrappers = new ArrayList<>(); 129 try { 130 Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); 131 if (interfaces != null) { 132 while (interfaces.hasMoreElements()) { 133 networkInterfaceWrappers.add( 134 new NetworkInterfaceWrapper(interfaces.nextElement())); 135 } 136 } 137 } catch (SocketException e) { 138 sharedLog.e("Failed to get network interfaces.", e); 139 } catch (NullPointerException e) { 140 // Android R has a bug that could lead to a NPE. See b/159277702. 141 sharedLog.e("Failed to call getNetworkInterfaces API", e); 142 } 143 144 return networkInterfaceWrappers; 145 } 146 147 @Nullable getAvailableNetwork()148 public Network getAvailableNetwork() { 149 return connectivityMonitor.getAvailableNetwork(); 150 } 151 152 /*** Check whether given network interface can support mdns */ canScanOnInterface(@ullable NetworkInterfaceWrapper networkInterface, @NonNull SharedLog sharedLog)153 private static boolean canScanOnInterface(@Nullable NetworkInterfaceWrapper networkInterface, 154 @NonNull SharedLog sharedLog) { 155 try { 156 if ((networkInterface == null) 157 || networkInterface.isLoopback() 158 || networkInterface.isPointToPoint() 159 || networkInterface.isVirtual() 160 || !networkInterface.isUp() 161 || !networkInterface.supportsMulticast()) { 162 return false; 163 } 164 return hasInet4Address(networkInterface) || hasInet6Address(networkInterface); 165 } catch (IOException e) { 166 sharedLog.e(String.format("Failed to check interface %s.", 167 networkInterface.getNetworkInterface().getDisplayName()), e); 168 } 169 170 return false; 171 } 172 hasInet4Address(@onNull NetworkInterfaceWrapper networkInterface)173 private static boolean hasInet4Address(@NonNull NetworkInterfaceWrapper networkInterface) { 174 for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) { 175 if (ifAddr.getAddress() instanceof Inet4Address) { 176 return true; 177 } 178 } 179 return false; 180 } 181 hasInet6Address(@onNull NetworkInterfaceWrapper networkInterface)182 private static boolean hasInet6Address(@NonNull NetworkInterfaceWrapper networkInterface) { 183 for (InterfaceAddress ifAddr : networkInterface.getInterfaceAddresses()) { 184 if (ifAddr.getAddress() instanceof Inet6Address) { 185 return true; 186 } 187 } 188 return false; 189 } 190 }