• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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