1 /* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import static org.junit.Assert.assertArrayEquals; 14 import static org.junit.Assert.assertEquals; 15 import static org.junit.Assert.assertFalse; 16 import static org.junit.Assert.assertNotNull; 17 import static org.junit.Assert.assertNull; 18 import static org.junit.Assert.assertTrue; 19 import static org.mockito.Mockito.CALLS_REAL_METHODS; 20 import static org.mockito.Mockito.mock; 21 22 import android.annotation.SuppressLint; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.net.ConnectivityManager; 26 import android.net.Network; 27 import android.net.NetworkCapabilities; 28 import android.net.NetworkRequest; 29 import android.os.Build; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.support.test.InstrumentationRegistry; 33 import androidx.annotation.Nullable; 34 import androidx.test.filters.MediumTest; 35 import androidx.test.filters.SmallTest; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Set; 41 import org.junit.Before; 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.webrtc.NetworkChangeDetector.ConnectionType; 45 import org.webrtc.NetworkChangeDetector.NetworkInformation; 46 import org.webrtc.NetworkMonitorAutoDetect.ConnectivityManagerDelegate; 47 import org.webrtc.NetworkMonitorAutoDetect.NetworkState; 48 import org.webrtc.NetworkMonitorAutoDetect.SimpleNetworkCallback; 49 50 /** 51 * Tests for org.webrtc.NetworkMonitor. 52 * 53 * TODO(deadbeef): These tests don't cover the interaction between 54 * NetworkManager.java and androidnetworkmonitor.cc, which is how this 55 * class is used in practice in WebRTC. 56 */ 57 @SuppressLint("NewApi") 58 public class NetworkMonitorTest { 59 private static final long INVALID_NET_ID = -1; 60 private NetworkChangeDetector detector; 61 private String fieldTrialsString = ""; 62 63 /** 64 * Listens for alerts fired by the NetworkMonitor when network status changes. 65 */ 66 private static class NetworkMonitorTestObserver implements NetworkMonitor.NetworkObserver { 67 private boolean receivedNotification; 68 69 @Override onConnectionTypeChanged(ConnectionType connectionType)70 public void onConnectionTypeChanged(ConnectionType connectionType) { 71 receivedNotification = true; 72 } 73 hasReceivedNotification()74 public boolean hasReceivedNotification() { 75 return receivedNotification; 76 } 77 resetHasReceivedNotification()78 public void resetHasReceivedNotification() { 79 receivedNotification = false; 80 } 81 } 82 83 /** 84 * Mocks out calls to the ConnectivityManager. 85 */ 86 private static class MockConnectivityManagerDelegate extends ConnectivityManagerDelegate { 87 private boolean activeNetworkExists; 88 private int networkType; 89 private int networkSubtype; 90 private int underlyingNetworkTypeForVpn; 91 private int underlyingNetworkSubtypeForVpn; 92 MockConnectivityManagerDelegate()93 MockConnectivityManagerDelegate() { 94 this(new HashSet<>(), ""); 95 } 96 MockConnectivityManagerDelegate(Set<Network> availableNetworks, String fieldTrialsString)97 MockConnectivityManagerDelegate(Set<Network> availableNetworks, String fieldTrialsString) { 98 super((ConnectivityManager) null, availableNetworks, fieldTrialsString); 99 } 100 101 @Override getNetworkState()102 public NetworkState getNetworkState() { 103 return new NetworkState(activeNetworkExists, networkType, networkSubtype, 104 underlyingNetworkTypeForVpn, underlyingNetworkSubtypeForVpn); 105 } 106 107 // Dummy implementations to avoid NullPointerExceptions in default implementations: 108 109 @Override getDefaultNetId()110 public long getDefaultNetId() { 111 return INVALID_NET_ID; 112 } 113 114 @Override getAllNetworks()115 public Network[] getAllNetworks() { 116 return new Network[0]; 117 } 118 119 @Override getNetworkState(Network network)120 public NetworkState getNetworkState(Network network) { 121 return new NetworkState(false, -1, -1, -1, -1); 122 } 123 setActiveNetworkExists(boolean networkExists)124 public void setActiveNetworkExists(boolean networkExists) { 125 activeNetworkExists = networkExists; 126 } 127 setNetworkType(int networkType)128 public void setNetworkType(int networkType) { 129 this.networkType = networkType; 130 } 131 setNetworkSubtype(int networkSubtype)132 public void setNetworkSubtype(int networkSubtype) { 133 this.networkSubtype = networkSubtype; 134 } 135 setUnderlyingNetworkType(int underlyingNetworkTypeForVpn)136 public void setUnderlyingNetworkType(int underlyingNetworkTypeForVpn) { 137 this.underlyingNetworkTypeForVpn = underlyingNetworkTypeForVpn; 138 } 139 setUnderlyingNetworkSubype(int underlyingNetworkSubtypeForVpn)140 public void setUnderlyingNetworkSubype(int underlyingNetworkSubtypeForVpn) { 141 this.underlyingNetworkSubtypeForVpn = underlyingNetworkSubtypeForVpn; 142 } 143 } 144 145 /** 146 * Mocks out calls to the WifiManager. 147 */ 148 private static class MockWifiManagerDelegate 149 extends NetworkMonitorAutoDetect.WifiManagerDelegate { 150 private String wifiSSID; 151 152 @Override getWifiSSID()153 public String getWifiSSID() { 154 return wifiSSID; 155 } 156 setWifiSSID(String wifiSSID)157 public void setWifiSSID(String wifiSSID) { 158 this.wifiSSID = wifiSSID; 159 } 160 } 161 162 // A dummy NetworkMonitorAutoDetect.Observer. 163 private static class TestNetworkMonitorAutoDetectObserver 164 extends NetworkMonitorAutoDetect.Observer { 165 final String fieldTrialsString; 166 TestNetworkMonitorAutoDetectObserver(String fieldTrialsString)167 TestNetworkMonitorAutoDetectObserver(String fieldTrialsString) { 168 this.fieldTrialsString = fieldTrialsString; 169 } 170 171 @Override onConnectionTypeChanged(ConnectionType newConnectionType)172 public void onConnectionTypeChanged(ConnectionType newConnectionType) {} 173 174 @Override onNetworkConnect(NetworkInformation networkInfo)175 public void onNetworkConnect(NetworkInformation networkInfo) {} 176 177 @Override onNetworkDisconnect(long networkHandle)178 public void onNetworkDisconnect(long networkHandle) {} 179 180 @Override onNetworkPreference(List<ConnectionType> types, @NetworkPreference int preference)181 public void onNetworkPreference(List<ConnectionType> types, @NetworkPreference int preference) { 182 } 183 184 // @Override 185 // public String getFieldTrialsString() { 186 // return fieldTrialsString; 187 // } 188 } 189 190 private NetworkMonitorAutoDetect receiver; 191 private MockConnectivityManagerDelegate connectivityDelegate; 192 private MockWifiManagerDelegate wifiDelegate; 193 194 /** 195 * Helper method to create a network monitor and delegates for testing. 196 */ createTestMonitor()197 private void createTestMonitor() { 198 Context context = InstrumentationRegistry.getTargetContext(); 199 200 NetworkMonitor.getInstance().setNetworkChangeDetectorFactory( 201 new NetworkChangeDetectorFactory() { 202 @Override 203 public NetworkChangeDetector create( 204 NetworkChangeDetector.Observer observer, Context context) { 205 detector = new NetworkMonitorAutoDetect(observer, context); 206 return detector; 207 } 208 }); 209 210 receiver = NetworkMonitor.createAndSetAutoDetectForTest(context, fieldTrialsString); 211 assertNotNull(receiver); 212 213 connectivityDelegate = new MockConnectivityManagerDelegate(); 214 connectivityDelegate.setActiveNetworkExists(true); 215 receiver.setConnectivityManagerDelegateForTests(connectivityDelegate); 216 217 wifiDelegate = new MockWifiManagerDelegate(); 218 receiver.setWifiManagerDelegateForTests(wifiDelegate); 219 wifiDelegate.setWifiSSID("foo"); 220 } 221 getCurrentConnectionType()222 private NetworkMonitorAutoDetect.ConnectionType getCurrentConnectionType() { 223 final NetworkMonitorAutoDetect.NetworkState networkState = receiver.getCurrentNetworkState(); 224 return NetworkMonitorAutoDetect.getConnectionType(networkState); 225 } 226 227 @Before setUp()228 public void setUp() { 229 ContextUtils.initialize(InstrumentationRegistry.getTargetContext()); 230 createTestMonitor(); 231 } 232 233 /** 234 * Tests that the receiver registers for connectivity intents during construction. 235 */ 236 @Test 237 @SmallTest testNetworkMonitorRegistersInConstructor()238 public void testNetworkMonitorRegistersInConstructor() throws InterruptedException { 239 Context context = InstrumentationRegistry.getTargetContext(); 240 241 NetworkMonitorAutoDetect.Observer observer = 242 new TestNetworkMonitorAutoDetectObserver(fieldTrialsString); 243 244 NetworkMonitorAutoDetect receiver = new NetworkMonitorAutoDetect(observer, context); 245 246 assertTrue(receiver.isReceiverRegisteredForTesting()); 247 } 248 249 /** 250 * Tests that when there is an intent indicating a change in network connectivity, it sends a 251 * notification to Java observers. 252 */ 253 @Test 254 @MediumTest testNetworkMonitorJavaObservers()255 public void testNetworkMonitorJavaObservers() throws InterruptedException { 256 // Initialize the NetworkMonitor with a connection. 257 Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 258 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 259 260 // We shouldn't be re-notified if the connection hasn't actually changed. 261 NetworkMonitorTestObserver observer = new NetworkMonitorTestObserver(); 262 NetworkMonitor.addNetworkObserver(observer); 263 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 264 assertFalse(observer.hasReceivedNotification()); 265 266 // We shouldn't be notified if we're connected to non-Wifi and the Wifi SSID changes. 267 wifiDelegate.setWifiSSID("bar"); 268 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 269 assertFalse(observer.hasReceivedNotification()); 270 271 // We should be notified when we change to Wifi. 272 connectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI); 273 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 274 assertTrue(observer.hasReceivedNotification()); 275 observer.resetHasReceivedNotification(); 276 277 // We should be notified when the Wifi SSID changes. 278 wifiDelegate.setWifiSSID("foo"); 279 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 280 assertTrue(observer.hasReceivedNotification()); 281 observer.resetHasReceivedNotification(); 282 283 // We shouldn't be re-notified if the Wifi SSID hasn't actually changed. 284 receiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent); 285 assertFalse(observer.hasReceivedNotification()); 286 287 // Mimic that connectivity has been lost and ensure that the observer gets the notification. 288 connectivityDelegate.setActiveNetworkExists(false); 289 Intent noConnectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 290 receiver.onReceive(InstrumentationRegistry.getTargetContext(), noConnectivityIntent); 291 assertTrue(observer.hasReceivedNotification()); 292 } 293 294 /** 295 * Tests that ConnectivityManagerDelegate doesn't crash. This test cannot rely on having any 296 * active network connections so it cannot usefully check results, but it can at least check 297 * that the functions don't crash. 298 */ 299 @Test 300 @SmallTest testConnectivityManagerDelegateDoesNotCrash()301 public void testConnectivityManagerDelegateDoesNotCrash() { 302 ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate( 303 InstrumentationRegistry.getTargetContext(), new HashSet<>(), fieldTrialsString); 304 delegate.getNetworkState(); 305 Network[] networks = delegate.getAllNetworks(); 306 if (networks.length >= 1) { 307 delegate.getNetworkState(networks[0]); 308 delegate.hasInternetCapability(networks[0]); 309 } 310 delegate.getDefaultNetId(); 311 } 312 313 /** Tests that ConnectivityManagerDelegate preferentially reads from the cache */ 314 @Test 315 @SmallTest testConnectivityManagerDelegatePreferentiallyReadsFromCache()316 public void testConnectivityManagerDelegatePreferentiallyReadsFromCache() { 317 final Set<Network> availableNetworks = new HashSet<>(); 318 ConnectivityManagerDelegate delegate = new ConnectivityManagerDelegate( 319 (ConnectivityManager) InstrumentationRegistry.getTargetContext().getSystemService( 320 Context.CONNECTIVITY_SERVICE), 321 availableNetworks, "getAllNetworksFromCache:true"); 322 323 Network[] networks = delegate.getAllNetworks(); 324 assertTrue(networks.length == 0); 325 326 final Network mockNetwork = mock(Network.class); 327 availableNetworks.add(mockNetwork); 328 329 assertArrayEquals(new Network[] {mockNetwork}, delegate.getAllNetworks()); 330 } 331 332 /** Tests field trial parsing */ 333 334 @Test 335 @SmallTest testConnectivityManager_requestVPN_disabled()336 public void testConnectivityManager_requestVPN_disabled() { 337 NetworkRequest request = 338 getNetworkRequestForFieldTrials("anyothertext,requestVPN:false,anyothertext"); 339 assertTrue(request.equals(new NetworkRequest.Builder() 340 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 341 .build())); 342 } 343 344 @Test 345 @SmallTest testConnectivityManager_requestVPN_enabled()346 public void testConnectivityManager_requestVPN_enabled() { 347 NetworkRequest request = getNetworkRequestForFieldTrials("requestVPN:true"); 348 assertTrue(request.equals(new NetworkRequest.Builder() 349 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 350 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 351 .build())); 352 } 353 354 @Test 355 @SmallTest testConnectivityManager_includeOtherUidNetworks_disabled()356 public void testConnectivityManager_includeOtherUidNetworks_disabled() { 357 NetworkRequest request = getNetworkRequestForFieldTrials("includeOtherUidNetworks:false"); 358 assertTrue(request.equals(new NetworkRequest.Builder() 359 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 360 .build())); 361 } 362 363 @Test 364 @SmallTest testConnectivityManager_includeOtherUidNetworks_enabled()365 public void testConnectivityManager_includeOtherUidNetworks_enabled() { 366 NetworkRequest request = getNetworkRequestForFieldTrials("includeOtherUidNetworks:true"); 367 NetworkRequest.Builder builder = 368 new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 369 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 370 builder.setIncludeOtherUidNetworks(true); 371 } 372 assertTrue(request.equals(builder.build())); 373 } 374 getNetworkRequestForFieldTrials(String fieldTrialsString)375 private NetworkRequest getNetworkRequestForFieldTrials(String fieldTrialsString) { 376 return new ConnectivityManagerDelegate( 377 (ConnectivityManager) null, new HashSet<>(), fieldTrialsString) 378 .createNetworkRequest(); 379 } 380 381 /** 382 * Tests that NetworkMonitorAutoDetect queryable APIs don't crash. This test cannot rely 383 * on having any active network connections so it cannot usefully check results, but it can at 384 * least check that the functions don't crash. 385 */ 386 @Test 387 @SmallTest testQueryableAPIsDoNotCrash()388 public void testQueryableAPIsDoNotCrash() { 389 NetworkMonitorAutoDetect.Observer observer = 390 new TestNetworkMonitorAutoDetectObserver(fieldTrialsString); 391 NetworkMonitorAutoDetect ncn = 392 new NetworkMonitorAutoDetect(observer, InstrumentationRegistry.getTargetContext()); 393 ncn.getDefaultNetId(); 394 } 395 396 /** 397 * Tests startMonitoring and stopMonitoring correctly set the autoDetect and number of observers. 398 */ 399 @Test 400 @SmallTest testStartStopMonitoring()401 public void testStartStopMonitoring() { 402 NetworkMonitor networkMonitor = NetworkMonitor.getInstance(); 403 Context context = ContextUtils.getApplicationContext(); 404 networkMonitor.startMonitoring(context, fieldTrialsString); 405 assertEquals(1, networkMonitor.getNumObservers()); 406 assertEquals(detector, networkMonitor.getNetworkChangeDetector()); 407 networkMonitor.stopMonitoring(); 408 assertEquals(0, networkMonitor.getNumObservers()); 409 assertNull(networkMonitor.getNetworkChangeDetector()); 410 } 411 } 412