• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
8 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
9 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
10 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
11 
12 import android.annotation.SuppressLint;
13 import android.app.Activity;
14 import android.content.BroadcastReceiver;
15 import android.content.Context;
16 import android.content.ContextWrapper;
17 import android.content.Intent;
18 import android.content.IntentFilter;
19 import android.net.ConnectivityManager;
20 import android.net.ConnectivityManager.NetworkCallback;
21 import android.net.Network;
22 import android.net.NetworkCapabilities;
23 import android.net.NetworkRequest;
24 import android.os.Build;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.StrictMode;
28 import android.telephony.TelephonyManager;
29 
30 import androidx.test.InstrumentationRegistry;
31 import androidx.test.filters.MediumTest;
32 
33 import org.junit.After;
34 import org.junit.Assert;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import org.chromium.base.ActivityState;
40 import org.chromium.base.ApplicationState;
41 import org.chromium.base.ApplicationStatus;
42 import org.chromium.base.ContextUtils;
43 import org.chromium.base.ThreadUtils;
44 import org.chromium.base.library_loader.LibraryLoader;
45 import org.chromium.base.library_loader.LibraryProcessType;
46 import org.chromium.base.test.BaseJUnit4ClassRunner;
47 import org.chromium.base.test.UiThreadTest;
48 import org.chromium.base.test.util.Feature;
49 import org.chromium.base.test.util.MinAndroidSdkLevel;
50 import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate;
51 import org.chromium.net.NetworkChangeNotifierAutoDetect.NetworkState;
52 import org.chromium.net.NetworkChangeNotifierAutoDetect.WifiManagerDelegate;
53 import org.chromium.net.test.util.NetworkChangeNotifierTestUtil;
54 
55 import java.lang.reflect.Constructor;
56 import java.lang.reflect.InvocationTargetException;
57 import java.util.ArrayList;
58 import java.util.concurrent.Callable;
59 import java.util.concurrent.FutureTask;
60 
61 /**
62  * Tests for org.chromium.net.NetworkChangeNotifier.
63  */
64 @RunWith(BaseJUnit4ClassRunner.class)
65 @SuppressLint("NewApi")
66 public class NetworkChangeNotifierTest {
67     /**
68      * Listens for alerts fired by the NetworkChangeNotifier when network status changes.
69      */
70     private static class NetworkChangeNotifierTestObserver
71             implements NetworkChangeNotifier.ConnectionTypeObserver {
72         private boolean mReceivedNotification;
73 
74         @Override
onConnectionTypeChanged(int connectionType)75         public void onConnectionTypeChanged(int connectionType) {
76             mReceivedNotification = true;
77         }
78 
hasReceivedNotification()79         public boolean hasReceivedNotification() {
80             return mReceivedNotification;
81         }
82 
resetHasReceivedNotification()83         public void resetHasReceivedNotification() {
84             mReceivedNotification = false;
85         }
86     }
87 
88     /**
89       * Listens for native notifications of max bandwidth change.
90       */
91     private static class TestNetworkChangeNotifier extends NetworkChangeNotifier {
92         @Override
notifyObserversOfConnectionSubtypeChange(int newConnectionSubtype)93         void notifyObserversOfConnectionSubtypeChange(int newConnectionSubtype) {
94             mReceivedConnectionSubtypeNotification = true;
95         }
96 
hasReceivedConnectionSubtypeNotification()97         public boolean hasReceivedConnectionSubtypeNotification() {
98             return mReceivedConnectionSubtypeNotification;
99         }
100 
resetHasReceivedConnectionSubtypeNotification()101         public void resetHasReceivedConnectionSubtypeNotification() {
102             mReceivedConnectionSubtypeNotification = false;
103         }
104 
105         private boolean mReceivedConnectionSubtypeNotification;
106     }
107 
108     private static class Helper {
109         private static final Constructor<Network> sNetworkConstructor;
110 
111         static {
112             try {
113                 sNetworkConstructor = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
114                         ? Network.class.getConstructor(Integer.TYPE)
115                         : null;
116             } catch (NoSuchMethodException | SecurityException e) {
117                 throw new RuntimeException("Unable to get Network constructor", e);
118             }
119         }
120 
getCapabilities(int transport)121         static NetworkCapabilities getCapabilities(int transport) {
122             // Create a NetworkRequest with corresponding capabilities
123             NetworkRequest request = new NetworkRequest.Builder()
124                                              .addCapability(NET_CAPABILITY_INTERNET)
125                                              .addTransportType(transport)
126                                              .build();
127             // Extract the NetworkCapabilities from the NetworkRequest.
128             try {
129                 return (NetworkCapabilities) request.getClass()
130                         .getDeclaredField("networkCapabilities")
131                         .get(request);
132             } catch (NoSuchFieldException | IllegalAccessException e) {
133                 return null;
134             }
135         }
136         // Create Network object given a NetID.
netIdToNetwork(int netId)137         static Network netIdToNetwork(int netId) {
138             try {
139                 return sNetworkConstructor.newInstance(netId);
140             } catch (
141             InstantiationException | InvocationTargetException | IllegalAccessException e) {
142                 throw new IllegalStateException("Trying to create Network when not allowed");
143             }
144         }
145     }
146 
triggerApplicationStateChange( final RegistrationPolicyApplicationStatus policy, final int applicationState)147     private static void triggerApplicationStateChange(
148             final RegistrationPolicyApplicationStatus policy, final int applicationState) {
149         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
150             @Override
151             public void run() {
152                 setApplicationHasVisibleActivities(
153                         applicationState == ApplicationState.HAS_RUNNING_ACTIVITIES);
154             }
155         });
156     }
157 
158     /**
159      * Mocks out calls to the ConnectivityManager.
160      */
161     private class MockConnectivityManagerDelegate extends ConnectivityManagerDelegate {
162         // A network we're pretending is currently connected.
163         private class MockNetwork {
164             // Network identifier
165             final int mNetId;
166             // Transport, one of android.net.NetworkCapabilities.TRANSPORT_*
167             final int mTransport;
168             // Is this VPN accessible to the current user?
169             final boolean mVpnAccessible;
170 
getCapabilities()171             NetworkCapabilities getCapabilities() {
172                 return Helper.getCapabilities(mTransport);
173             }
174 
175             /**
176              * @param netId Network identifier
177              * @param transport Transport, one of android.net.NetworkCapabilities.TRANSPORT_*
178              * @param vpnAccessible Is this VPN accessible to the current user?
179              */
MockNetwork(int netId, int transport, boolean vpnAccessible)180             MockNetwork(int netId, int transport, boolean vpnAccessible) {
181                 mNetId = netId;
182                 mTransport = transport;
183                 mVpnAccessible = vpnAccessible;
184             }
185         }
186 
187         // List of networks we're pretending are currently connected.
188         private final ArrayList<MockNetwork> mMockNetworks = new ArrayList<>();
189 
190         private boolean mActiveNetworkExists;
191         private int mNetworkType;
192         private int mNetworkSubtype;
193         private boolean mIsMetered;
194         private boolean mIsPrivateDnsActive;
195         private String mPrivateDnsServerName;
196         private NetworkCallback mLastRegisteredNetworkCallback;
197         private NetworkCallback mLastRegisteredDefaultNetworkCallback;
198 
199         @Override
getNetworkState(WifiManagerDelegate wifiManagerDelegate)200         public NetworkState getNetworkState(WifiManagerDelegate wifiManagerDelegate) {
201             return new NetworkState(mActiveNetworkExists, mNetworkType, mNetworkSubtype, mIsMetered,
202                     mNetworkType == ConnectivityManager.TYPE_WIFI
203                             ? wifiManagerDelegate.getWifiSsid()
204                             : null,
205                     mIsPrivateDnsActive, mPrivateDnsServerName);
206         }
207 
208         @Override
getNetworkCapabilities(Network network)209         protected NetworkCapabilities getNetworkCapabilities(Network network) {
210             int netId = demungeNetId(NetworkChangeNotifierAutoDetect.networkToNetId(network));
211             for (MockNetwork mockNetwork : mMockNetworks) {
212                 if (netId == mockNetwork.mNetId) {
213                     return mockNetwork.getCapabilities();
214                 }
215             }
216             return null;
217         }
218 
219         @Override
vpnAccessible(Network network)220         protected boolean vpnAccessible(Network network) {
221             int netId = demungeNetId(NetworkChangeNotifierAutoDetect.networkToNetId(network));
222             for (MockNetwork mockNetwork : mMockNetworks) {
223                 if (netId == mockNetwork.mNetId) {
224                     return mockNetwork.mVpnAccessible;
225                 }
226             }
227             return false;
228         }
229 
230         @Override
getAllNetworksUnfiltered()231         protected Network[] getAllNetworksUnfiltered() {
232             Network[] networks = new Network[mMockNetworks.size()];
233             for (int i = 0; i < networks.length; i++) {
234                 networks[i] = Helper.netIdToNetwork(mMockNetworks.get(i).mNetId);
235             }
236             return networks;
237         }
238 
239         // Dummy implementations to avoid NullPointerExceptions in default implementations:
240 
241         @Override
getDefaultNetwork()242         public Network getDefaultNetwork() {
243             return null;
244         }
245 
246         @Override
getConnectionType(Network network)247         public int getConnectionType(Network network) {
248             return ConnectionType.CONNECTION_NONE;
249         }
250 
251         @Override
unregisterNetworkCallback(NetworkCallback networkCallback)252         public void unregisterNetworkCallback(NetworkCallback networkCallback) {}
253 
254         // Dummy implementation that also records the last registered callback.
255         @Override
registerNetworkCallback( NetworkRequest networkRequest, NetworkCallback networkCallback, Handler handler)256         public void registerNetworkCallback(
257                 NetworkRequest networkRequest, NetworkCallback networkCallback, Handler handler) {
258             mLastRegisteredNetworkCallback = networkCallback;
259         }
260 
261         // Dummy implementation that also records the last registered callback.
262         @Override
registerDefaultNetworkCallback( NetworkCallback networkCallback, Handler handler)263         public void registerDefaultNetworkCallback(
264                 NetworkCallback networkCallback, Handler handler) {
265             mLastRegisteredDefaultNetworkCallback = networkCallback;
266         }
267 
setActiveNetworkExists(boolean networkExists)268         public void setActiveNetworkExists(boolean networkExists) {
269             mActiveNetworkExists = networkExists;
270         }
271 
setNetworkType(int networkType)272         public void setNetworkType(int networkType) {
273             mNetworkType = networkType;
274         }
275 
setIsMetered(boolean isMetered)276         public void setIsMetered(boolean isMetered) {
277             mIsMetered = isMetered;
278         }
279 
setNetworkSubtype(int networkSubtype)280         public void setNetworkSubtype(int networkSubtype) {
281             mNetworkSubtype = networkSubtype;
282         }
283 
setIsPrivateDnsActive(boolean isPrivateDnsActive)284         public void setIsPrivateDnsActive(boolean isPrivateDnsActive) {
285             mIsPrivateDnsActive = isPrivateDnsActive;
286         }
287 
setPrivateDnsServerName(String privateDnsServerName)288         public void setPrivateDnsServerName(String privateDnsServerName) {
289             mPrivateDnsServerName = privateDnsServerName;
290         }
291 
getLastRegisteredNetworkCallback()292         public NetworkCallback getLastRegisteredNetworkCallback() {
293             return mLastRegisteredNetworkCallback;
294         }
295 
getDefaultNetworkCallback()296         public NetworkCallback getDefaultNetworkCallback() {
297             return mLastRegisteredDefaultNetworkCallback;
298         }
299 
300         /**
301          * Pretends a network connects.
302          * @param netId Network identifier
303          * @param transport Transport, one of android.net.NetworkCapabilities.TRANSPORT_*
304          * @param vpnAccessible Is this VPN accessible to the current user?
305          */
addNetwork(int netId, int transport, boolean vpnAccessible)306         public void addNetwork(int netId, int transport, boolean vpnAccessible) {
307             mMockNetworks.add(new MockNetwork(netId, transport, vpnAccessible));
308             mLastRegisteredNetworkCallback.onAvailable(Helper.netIdToNetwork(netId));
309         }
310 
311         /**
312          * Pretends a network disconnects.
313          * @param netId Network identifier
314          */
removeNetwork(int netId)315         public void removeNetwork(int netId) {
316             for (MockNetwork mockNetwork : mMockNetworks) {
317                 if (mockNetwork.mNetId == netId) {
318                     mMockNetworks.remove(mockNetwork);
319                     mLastRegisteredNetworkCallback.onLost(Helper.netIdToNetwork(netId));
320                     break;
321                 }
322             }
323         }
324     }
325 
326     /**
327      * Mocks out calls to the WifiManager.
328      */
329     private static class MockWifiManagerDelegate extends WifiManagerDelegate {
330         private String mWifiSSID;
331 
332         @Override
getWifiSsid()333         public String getWifiSsid() {
334             return mWifiSSID;
335         }
336 
setWifiSSID(String wifiSSID)337         public void setWifiSSID(String wifiSSID) {
338             mWifiSSID = wifiSSID;
339         }
340     }
341 
demungeNetId(long netId)342     private static int demungeNetId(long netId) {
343         // On Marshmallow, demunge the NetID to undo munging done in Network.getNetworkHandle().
344         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
345             netId >>= 32;
346         }
347         // Now that the NetID has been demunged it is a true NetID which means it's only a 16-bit
348         // value (see ConnectivityService.MAX_NET_ID) so it should be safe to cast to int.
349         return (int) netId;
350     }
351 
352     // Types of network changes. Each is associated with a NetworkChangeNotifierAutoDetect.Observer
353     // callback, and NONE is provided to indicate no callback observed.
354     private static enum ChangeType { NONE, CONNECT, SOON_TO_DISCONNECT, DISCONNECT, PURGE_LIST }
355 
356     // Recorded information about a network change that took place.
357     private static class ChangeInfo {
358         // The type of change.
359         final ChangeType mChangeType;
360         // The network identifier of the network changing.
361         final int mNetId;
362 
363         /**
364          * @param changeType the type of change.
365          * @param netId the network identifier of the network changing.
366          */
ChangeInfo(ChangeType changeType, long netId)367         ChangeInfo(ChangeType changeType, long netId) {
368             mChangeType = changeType;
369             mNetId = demungeNetId(netId);
370         }
371     }
372 
373     // NetworkChangeNotifierAutoDetect.Observer used to verify proper notifications are sent out.
374     // Notifications come back on UI thread. assertLastChange() called on test thread.
375     private static class TestNetworkChangeNotifierAutoDetectObserver
376             implements NetworkChangeNotifierAutoDetect.Observer {
377         // The list of network changes that have been witnessed.
378         final ArrayList<ChangeInfo> mChanges = new ArrayList<>();
379 
380         @Override
onConnectionTypeChanged(int newConnectionType)381         public void onConnectionTypeChanged(int newConnectionType) {}
382         @Override
onConnectionCostChanged(int newConnectionCost)383         public void onConnectionCostChanged(int newConnectionCost) {}
384         @Override
onConnectionSubtypeChanged(int newConnectionSubtype)385         public void onConnectionSubtypeChanged(int newConnectionSubtype) {}
386 
387         @Override
onNetworkConnect(long netId, int connectionType)388         public void onNetworkConnect(long netId, int connectionType) {
389             ThreadUtils.assertOnUiThread();
390             mChanges.add(new ChangeInfo(ChangeType.CONNECT, netId));
391         }
392 
393         @Override
onNetworkSoonToDisconnect(long netId)394         public void onNetworkSoonToDisconnect(long netId) {
395             ThreadUtils.assertOnUiThread();
396             mChanges.add(new ChangeInfo(ChangeType.SOON_TO_DISCONNECT, netId));
397         }
398 
399         @Override
onNetworkDisconnect(long netId)400         public void onNetworkDisconnect(long netId) {
401             ThreadUtils.assertOnUiThread();
402             mChanges.add(new ChangeInfo(ChangeType.DISCONNECT, netId));
403         }
404 
405         @Override
purgeActiveNetworkList(long[] activeNetIds)406         public void purgeActiveNetworkList(long[] activeNetIds) {
407             ThreadUtils.assertOnUiThread();
408             if (activeNetIds.length == 1) {
409                 mChanges.add(new ChangeInfo(ChangeType.PURGE_LIST, activeNetIds[0]));
410             } else {
411                 mChanges.add(new ChangeInfo(ChangeType.PURGE_LIST, NetId.INVALID));
412             }
413         }
414 
415         // Verify last notification was the expected one.
assertLastChange(ChangeType type, int netId)416         public void assertLastChange(ChangeType type, int netId) throws Exception {
417             // Make sure notification processed.
418             NetworkChangeNotifierTestUtil.flushUiThreadTaskQueue();
419             Assert.assertNotNull(mChanges.get(0));
420             Assert.assertEquals(type, mChanges.get(0).mChangeType);
421             Assert.assertEquals(netId, mChanges.get(0).mNetId);
422             mChanges.clear();
423         }
424     }
425 
426     // Activity used to send updates to ApplicationStatus to convince ApplicationStatus that the app
427     // is in the foreground or background. Only accessed on the UI thread.
428     private static Activity sActivity;
429 
430     // Network.Network(int netId) pointer.
431     private TestNetworkChangeNotifier mNotifier;
432     private NetworkChangeNotifierAutoDetect mReceiver;
433     private MockConnectivityManagerDelegate mConnectivityDelegate;
434     private MockWifiManagerDelegate mWifiDelegate;
435 
436     private static enum WatchForChanges {
437         ALWAYS,
438         ONLY_WHEN_APP_IN_FOREGROUND,
439     }
440 
441     /**
442      * Helper method to create a notifier and delegates for testing.
443      * @param watchForChanges indicates whether app wants to watch for changes always or only when
444      *            it is in the foreground.
445      */
createTestNotifier(WatchForChanges watchForChanges)446     private void createTestNotifier(WatchForChanges watchForChanges) {
447         Context context = new ContextWrapper(InstrumentationRegistry.getInstrumentation()
448                                                      .getTargetContext()
449                                                      .getApplicationContext()) {
450             // Mock out to avoid unintended system interaction.
451             @Override
452             public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
453                     String permission, Handler scheduler, int flags) {
454                 // Should not be used starting with Pie.
455                 Assert.assertFalse(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P);
456                 return null;
457             }
458 
459             @Override
460             public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
461                     String permission, Handler scheduler) {
462                 return registerReceiver(receiver, filter, permission, scheduler, 0);
463             }
464 
465             @Override
466             public void unregisterReceiver(BroadcastReceiver receiver) {}
467 
468             // Don't allow escaping the mock via the application context.
469             @Override
470             public Context getApplicationContext() {
471                 return this;
472             }
473         };
474         ContextUtils.initApplicationContextForTests(context);
475         mNotifier = new TestNetworkChangeNotifier();
476         NetworkChangeNotifier.resetInstanceForTests(mNotifier);
477         if (watchForChanges == WatchForChanges.ALWAYS) {
478             NetworkChangeNotifier.registerToReceiveNotificationsAlways();
479         } else {
480             NetworkChangeNotifier.setAutoDetectConnectivityState(true);
481         }
482         mReceiver = NetworkChangeNotifier.getAutoDetectorForTest();
483         Assert.assertNotNull(mReceiver);
484 
485         mConnectivityDelegate =
486                 new MockConnectivityManagerDelegate();
487         mConnectivityDelegate.setActiveNetworkExists(true);
488         mReceiver.setConnectivityManagerDelegateForTests(mConnectivityDelegate);
489 
490         mWifiDelegate = new MockWifiManagerDelegate();
491         mReceiver.setWifiManagerDelegateForTests(mWifiDelegate);
492         mWifiDelegate.setWifiSSID("foo");
493     }
494 
getCurrentConnectionSubtype()495     private int getCurrentConnectionSubtype() {
496         return mReceiver.getCurrentNetworkState().getConnectionSubtype();
497     }
498 
getCurrentConnectionType()499     private int getCurrentConnectionType() {
500         return mReceiver.getCurrentNetworkState().getConnectionType();
501     }
502 
getCurrentConnectionCost()503     private int getCurrentConnectionCost() {
504         return mReceiver.getCurrentNetworkState().getConnectionCost();
505     }
506 
507     @Before
setUp()508     public void setUp() throws Throwable {
509         LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER);
510         LibraryLoader.getInstance().ensureInitialized();
511 
512         ThreadUtils.runOnUiThreadBlocking(() -> {
513             if (sActivity == null) {
514                 sActivity = new Activity();
515                 if (!ApplicationStatus.isInitialized()) {
516                     ApplicationStatus.initialize(BaseJUnit4ClassRunner.getApplication());
517                 }
518                 ApplicationStatus.onStateChangeForTesting(sActivity, ActivityState.CREATED);
519             }
520             setApplicationHasVisibleActivities(false);
521             createTestNotifier(WatchForChanges.ONLY_WHEN_APP_IN_FOREGROUND);
522         });
523     }
524 
525     @After
tearDown()526     public void tearDown() {
527         // Reset the network change notifier.
528         NetworkChangeNotifier.resetInstanceForTests();
529     }
530 
531     /**
532      * Allow tests to simulate the application being foregrounded or backgrounded.
533      */
setApplicationHasVisibleActivities(boolean hasVisibleActivities)534     private static void setApplicationHasVisibleActivities(boolean hasVisibleActivities) {
535         ThreadUtils.assertOnUiThread();
536         ApplicationStatus.onStateChangeForTesting(
537                 sActivity, hasVisibleActivities ? ActivityState.STARTED : ActivityState.STOPPED);
538     }
539 
540     /**
541      * Tests that the receiver registers for connectivity
542      * broadcasts during construction when the registration policy dictates.
543      */
544     @Test
545     @UiThreadTest
546     @MediumTest
547     @Feature({"Android-AppBase"})
testNetworkChangeNotifierRegistersWhenPolicyDictates()548     public void testNetworkChangeNotifierRegistersWhenPolicyDictates() {
549         NetworkChangeNotifierAutoDetect.Observer observer =
550                 new TestNetworkChangeNotifierAutoDetectObserver();
551 
552         setApplicationHasVisibleActivities(true);
553         NetworkChangeNotifierAutoDetect receiver = new NetworkChangeNotifierAutoDetect(
554                 observer, new RegistrationPolicyApplicationStatus());
555 
556         Assert.assertTrue(receiver.isReceiverRegisteredForTesting());
557 
558         setApplicationHasVisibleActivities(false);
559         receiver = new NetworkChangeNotifierAutoDetect(
560                 observer, new RegistrationPolicyApplicationStatus());
561 
562         Assert.assertFalse(receiver.isReceiverRegisteredForTesting());
563     }
564 
565     /**
566      * Tests that the receiver toggles registration for connectivity intents based on activity
567      * state.
568      */
569     @Test
570     @UiThreadTest
571     @MediumTest
572     @Feature({"Android-AppBase"})
testNetworkChangeNotifierRegistersForIntents()573     public void testNetworkChangeNotifierRegistersForIntents() {
574         RegistrationPolicyApplicationStatus policy =
575                 (RegistrationPolicyApplicationStatus) mReceiver.getRegistrationPolicy();
576         triggerApplicationStateChange(policy, ApplicationState.HAS_RUNNING_ACTIVITIES);
577         Assert.assertTrue(mReceiver.isReceiverRegisteredForTesting());
578 
579         triggerApplicationStateChange(policy, ApplicationState.HAS_PAUSED_ACTIVITIES);
580         Assert.assertFalse(mReceiver.isReceiverRegisteredForTesting());
581 
582         triggerApplicationStateChange(policy, ApplicationState.HAS_RUNNING_ACTIVITIES);
583         Assert.assertTrue(mReceiver.isReceiverRegisteredForTesting());
584     }
585 
586     /**
587      * Tests that getCurrentConnectionCost() returns the correct result.
588      */
589     @Test
590     @UiThreadTest
591     @MediumTest
592     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionCost()593     public void testNetworkChangeNotifierConnectionCost() {
594         mConnectivityDelegate.setIsMetered(true);
595         Assert.assertEquals(ConnectionCost.METERED, getCurrentConnectionCost());
596         mConnectivityDelegate.setIsMetered(false);
597         Assert.assertEquals(ConnectionCost.UNMETERED, getCurrentConnectionCost());
598     }
599 
600     /**
601      * Tests that changing the network type changes the connection subtype.
602      */
603     @Test
604     @UiThreadTest
605     @MediumTest
606     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeEthernet()607     public void testNetworkChangeNotifierConnectionSubtypeEthernet() {
608         // Show that for Ethernet the link speed is unknown (+Infinity).
609         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_ETHERNET);
610         Assert.assertEquals(ConnectionType.CONNECTION_ETHERNET, getCurrentConnectionType());
611         Assert.assertEquals(ConnectionSubtype.SUBTYPE_UNKNOWN, getCurrentConnectionSubtype());
612     }
613 
614     @Test
615     @UiThreadTest
616     @MediumTest
617     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeWifi()618     public void testNetworkChangeNotifierConnectionSubtypeWifi() {
619         // Show that for WiFi the link speed is unknown (+Infinity).
620         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI);
621         Assert.assertEquals(ConnectionType.CONNECTION_WIFI, getCurrentConnectionType());
622         Assert.assertEquals(ConnectionSubtype.SUBTYPE_UNKNOWN, getCurrentConnectionSubtype());
623     }
624 
625     @Test
626     @UiThreadTest
627     @MediumTest
628     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeWiMax()629     public void testNetworkChangeNotifierConnectionSubtypeWiMax() {
630         // Show that for WiMax the link speed is unknown (+Infinity), although the type is 4g.
631         // TODO(jkarlin): Add support for CONNECTION_WIMAX as specified in
632         // http://w3c.github.io/netinfo/.
633         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIMAX);
634         Assert.assertEquals(ConnectionType.CONNECTION_4G, getCurrentConnectionType());
635         Assert.assertEquals(ConnectionSubtype.SUBTYPE_UNKNOWN, getCurrentConnectionSubtype());
636     }
637 
638     @Test
639     @UiThreadTest
640     @MediumTest
641     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeBluetooth()642     public void testNetworkChangeNotifierConnectionSubtypeBluetooth() {
643         // Show that for bluetooth the link speed is unknown (+Infinity).
644         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_BLUETOOTH);
645         Assert.assertEquals(ConnectionType.CONNECTION_BLUETOOTH, getCurrentConnectionType());
646         Assert.assertEquals(ConnectionSubtype.SUBTYPE_UNKNOWN, getCurrentConnectionSubtype());
647     }
648 
649     @Test
650     @UiThreadTest
651     @MediumTest
652     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeMobile()653     public void testNetworkChangeNotifierConnectionSubtypeMobile() {
654         // Test that for mobile types the subtype is used to determine the connection subtype.
655         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_MOBILE);
656         mConnectivityDelegate.setNetworkSubtype(TelephonyManager.NETWORK_TYPE_LTE);
657         Assert.assertEquals(ConnectionType.CONNECTION_4G, getCurrentConnectionType());
658         Assert.assertEquals(ConnectionSubtype.SUBTYPE_LTE, getCurrentConnectionSubtype());
659     }
660 
661     /**
662      * Indicate to NetworkChangeNotifierAutoDetect that a connectivity change has occurred.
663      * Uses same signals that system would use.
664      */
notifyConnectivityChange()665     private void notifyConnectivityChange() {
666         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
667             mConnectivityDelegate.getDefaultNetworkCallback().onAvailable(null);
668         } else {
669             Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
670             mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
671         }
672     }
673 
674     /**
675      * Tests that when Chrome gets an intent indicating a change in network connectivity, it sends a
676      * notification to Java observers.
677      */
678     @Test
679     @UiThreadTest
680     @MediumTest
681     @Feature({"Android-AppBase"})
testNetworkChangeNotifierJavaObservers()682     public void testNetworkChangeNotifierJavaObservers() {
683         mReceiver.register();
684         // Initialize the NetworkChangeNotifier with a connection.
685         Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
686         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
687 
688         // We shouldn't be re-notified if the connection hasn't actually changed.
689         NetworkChangeNotifierTestObserver observer = new NetworkChangeNotifierTestObserver();
690         NetworkChangeNotifier.addConnectionTypeObserver(observer);
691         notifyConnectivityChange();
692         Assert.assertFalse(observer.hasReceivedNotification());
693 
694         // We shouldn't be notified if we're connected to non-Wifi and the Wifi SSID changes.
695         mWifiDelegate.setWifiSSID("bar");
696         notifyConnectivityChange();
697         Assert.assertFalse(observer.hasReceivedNotification());
698         // We should be notified when we change to Wifi.
699         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI);
700         notifyConnectivityChange();
701         Assert.assertTrue(observer.hasReceivedNotification());
702         observer.resetHasReceivedNotification();
703         // We should be notified when the Wifi SSID changes.
704         mWifiDelegate.setWifiSSID("foo");
705         notifyConnectivityChange();
706         Assert.assertTrue(observer.hasReceivedNotification());
707         observer.resetHasReceivedNotification();
708         // We shouldn't be re-notified if the Wifi SSID hasn't actually changed.
709         notifyConnectivityChange();
710         Assert.assertFalse(observer.hasReceivedNotification());
711 
712         // We should be notified if use of DNS-over-TLS changes.
713         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
714             // Verify notification for enabling private DNS.
715             mConnectivityDelegate.setIsPrivateDnsActive(true);
716             mConnectivityDelegate.getDefaultNetworkCallback().onLinkPropertiesChanged(null, null);
717             Assert.assertTrue(observer.hasReceivedNotification());
718             observer.resetHasReceivedNotification();
719             // Verify notification for specifying private DNS server.
720             mConnectivityDelegate.setPrivateDnsServerName("dotserver.com");
721             mConnectivityDelegate.getDefaultNetworkCallback().onLinkPropertiesChanged(null, null);
722             Assert.assertTrue(observer.hasReceivedNotification());
723             observer.resetHasReceivedNotification();
724             // Verify no notification for no change.
725             mConnectivityDelegate.getDefaultNetworkCallback().onLinkPropertiesChanged(null, null);
726             Assert.assertFalse(observer.hasReceivedNotification());
727             // Verify notification for disabling.
728             mConnectivityDelegate.setIsPrivateDnsActive(false);
729             mConnectivityDelegate.getDefaultNetworkCallback().onLinkPropertiesChanged(null, null);
730             Assert.assertTrue(observer.hasReceivedNotification());
731             observer.resetHasReceivedNotification();
732         }
733 
734         // Mimic that connectivity has been lost and ensure that Chrome notifies our observer.
735         mConnectivityDelegate.setActiveNetworkExists(false);
736         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
737             mConnectivityDelegate.getDefaultNetworkCallback().onLost(null);
738         } else {
739             mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
740         }
741         Assert.assertTrue(observer.hasReceivedNotification());
742 
743         observer.resetHasReceivedNotification();
744         // Pretend we got moved to the background.
745         final RegistrationPolicyApplicationStatus policy =
746                 (RegistrationPolicyApplicationStatus) mReceiver.getRegistrationPolicy();
747         triggerApplicationStateChange(policy, ApplicationState.HAS_PAUSED_ACTIVITIES);
748         // Change the state.
749         mConnectivityDelegate.setActiveNetworkExists(true);
750         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI);
751         // The NetworkChangeNotifierAutoDetect doesn't receive any notification while we are in the
752         // background, but when we get back to the foreground the state changed should be detected
753         // and a notification sent.
754         triggerApplicationStateChange(policy, ApplicationState.HAS_RUNNING_ACTIVITIES);
755         Assert.assertTrue(observer.hasReceivedNotification());
756     }
757 
758     /**
759      * Tests that when Chrome gets an intent indicating a change in max bandwidth, it sends a
760      * notification to Java observers.
761      */
762     @Test
763     @UiThreadTest
764     @MediumTest
765     @Feature({"Android-AppBase"})
testNetworkChangeNotifierConnectionSubtypeNotifications()766     public void testNetworkChangeNotifierConnectionSubtypeNotifications() {
767         mReceiver.register();
768         // Initialize the NetworkChangeNotifier with a connection.
769         mConnectivityDelegate.setActiveNetworkExists(true);
770         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI);
771         Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
772         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
773         Assert.assertTrue(mNotifier.hasReceivedConnectionSubtypeNotification());
774         mNotifier.resetHasReceivedConnectionSubtypeNotification();
775 
776         // We shouldn't be re-notified if the connection hasn't actually changed.
777         NetworkChangeNotifierTestObserver observer = new NetworkChangeNotifierTestObserver();
778         NetworkChangeNotifier.addConnectionTypeObserver(observer);
779         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
780         Assert.assertFalse(mNotifier.hasReceivedConnectionSubtypeNotification());
781 
782         // We should be notified if bandwidth and connection type changed.
783         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_ETHERNET);
784         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
785         Assert.assertTrue(mNotifier.hasReceivedConnectionSubtypeNotification());
786         mNotifier.resetHasReceivedConnectionSubtypeNotification();
787 
788         // We should be notified if the connection type changed, but not the bandwidth.
789         // Note that TYPE_ETHERNET and TYPE_BLUETOOTH have the same +INFINITY max bandwidth.
790         // This test will fail if that changes.
791         mConnectivityDelegate.setNetworkType(ConnectivityManager.TYPE_BLUETOOTH);
792         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
793         Assert.assertTrue(mNotifier.hasReceivedConnectionSubtypeNotification());
794     }
795 
796     /**
797      * Tests that when setting {@code registerToReceiveNotificationsAlways()},
798      * a NetworkChangeNotifierAutoDetect object is successfully created.
799      */
800     @Test
801     @UiThreadTest
802     @MediumTest
803     @Feature({"Android-AppBase"})
testCreateNetworkChangeNotifierAlwaysWatchForChanges()804     public void testCreateNetworkChangeNotifierAlwaysWatchForChanges() {
805         createTestNotifier(WatchForChanges.ALWAYS);
806         Assert.assertTrue(mReceiver.isReceiverRegisteredForTesting());
807 
808         // Make sure notifications can be received.
809         NetworkChangeNotifierTestObserver observer = new NetworkChangeNotifierTestObserver();
810         NetworkChangeNotifier.addConnectionTypeObserver(observer);
811         Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
812         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), connectivityIntent);
813         Assert.assertTrue(observer.hasReceivedNotification());
814     }
815 
816     /**
817      * Tests that ConnectivityManagerDelegate doesn't crash. This test cannot rely on having any
818      * active network connections so it cannot usefully check results, but it can at least check
819      * that the functions don't crash.
820      */
821     @Test
822     @UiThreadTest
823     @MediumTest
824     @Feature({"Android-AppBase"})
testConnectivityManagerDelegateDoesNotCrash()825     public void testConnectivityManagerDelegateDoesNotCrash() {
826         ConnectivityManagerDelegate delegate =
827                 new ConnectivityManagerDelegate(InstrumentationRegistry.getTargetContext());
828         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
829             delegate.getNetworkState(null);
830         } else {
831             delegate.getNetworkState(
832                     new WifiManagerDelegate(InstrumentationRegistry.getTargetContext()));
833         }
834         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
835             // getConnectionType(Network) doesn't crash upon invalid Network argument.
836             Network invalidNetwork = Helper.netIdToNetwork(NetId.INVALID);
837             Assert.assertEquals(
838                     ConnectionType.CONNECTION_NONE, delegate.getConnectionType(invalidNetwork));
839 
840             Network[] networks = delegate.getAllNetworksUnfiltered();
841             Assert.assertNotNull(networks);
842             if (networks.length >= 1) {
843                 delegate.getConnectionType(networks[0]);
844             }
845             delegate.getDefaultNetwork();
846             NetworkCallback networkCallback = new NetworkCallback();
847             NetworkRequest networkRequest = new NetworkRequest.Builder().build();
848             delegate.registerNetworkCallback(
849                     networkRequest, networkCallback, new Handler(Looper.myLooper()));
850             delegate.unregisterNetworkCallback(networkCallback);
851         }
852     }
853 
854     /**
855      * Tests that NetworkChangeNotifierAutoDetect queryable APIs don't crash. This test cannot rely
856      * on having any active network connections so it cannot usefully check results, but it can at
857      * least check that the functions don't crash.
858      */
859     @Test
860     @UiThreadTest
861     @MediumTest
862     @Feature({"Android-AppBase"})
testQueryableAPIsDoNotCrash()863     public void testQueryableAPIsDoNotCrash() {
864         NetworkChangeNotifierAutoDetect.Observer observer =
865                 new TestNetworkChangeNotifierAutoDetectObserver();
866         NetworkChangeNotifierAutoDetect ncn = new NetworkChangeNotifierAutoDetect(observer,
867                 new RegistrationPolicyAlwaysRegister());
868         ncn.getNetworksAndTypes();
869         ncn.getDefaultNetId();
870     }
871 
872     /**
873      * Tests that NetworkChangeNotifierAutoDetect query-able APIs return expected
874      * values from the inserted mock ConnectivityManager.
875      */
876     @Test
877     @UiThreadTest
878     @MediumTest
879     @Feature({"Android-AppBase"})
testQueryableAPIsReturnExpectedValuesFromMockDelegate()880     public void testQueryableAPIsReturnExpectedValuesFromMockDelegate() {
881         NetworkChangeNotifierAutoDetect.Observer observer =
882                 new TestNetworkChangeNotifierAutoDetectObserver();
883 
884         setApplicationHasVisibleActivities(false);
885         NetworkChangeNotifierAutoDetect ncn = new NetworkChangeNotifierAutoDetect(
886                 observer, new RegistrationPolicyApplicationStatus());
887 
888         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
889             Assert.assertEquals(0, ncn.getNetworksAndTypes().length);
890             Assert.assertEquals(NetId.INVALID, ncn.getDefaultNetId());
891             return;
892         }
893 
894         // Insert a mocked dummy implementation for the ConnectivityDelegate.
895         ncn.setConnectivityManagerDelegateForTests(new ConnectivityManagerDelegate() {
896             public final Network[] mNetworks =
897                     new Network[] {Helper.netIdToNetwork(111), Helper.netIdToNetwork(333)};
898 
899             @Override
900             protected Network[] getAllNetworksUnfiltered() {
901                 return mNetworks;
902             }
903 
904             @Override
905             Network getDefaultNetwork() {
906                 return mNetworks[1];
907             }
908 
909             @Override
910             protected NetworkCapabilities getNetworkCapabilities(Network network) {
911                 return Helper.getCapabilities(TRANSPORT_WIFI);
912             }
913 
914             @Override
915             public int getConnectionType(Network network) {
916                 return ConnectionType.CONNECTION_NONE;
917             }
918         });
919 
920         // Verify that the mock delegate connectivity manager is being used
921         // by the network change notifier auto-detector.
922         Assert.assertEquals(333, demungeNetId(ncn.getDefaultNetId()));
923 
924         // The api {@link NetworkChangeNotifierAutoDetect#getNetworksAndTypes()}
925         // returns an array of a repeated sequence of: (NetID, ConnectionType).
926         // There are 4 entries in the array, two for each network.
927         Assert.assertEquals(4, ncn.getNetworksAndTypes().length);
928         Assert.assertEquals(111, demungeNetId(ncn.getNetworksAndTypes()[0]));
929         Assert.assertEquals(ConnectionType.CONNECTION_NONE, ncn.getNetworksAndTypes()[1]);
930         Assert.assertEquals(333, demungeNetId(ncn.getNetworksAndTypes()[2]));
931         Assert.assertEquals(ConnectionType.CONNECTION_NONE, ncn.getNetworksAndTypes()[3]);
932     }
933 
934     /**
935      * Tests that callbacks are issued to Observers when NetworkChangeNotifierAutoDetect receives
936      * the right signals (via its NetworkCallback).
937      */
938     @Test
939     @MediumTest
940     @Feature({"Android-AppBase"})
941     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
testNetworkCallbacks()942     public void testNetworkCallbacks() throws Exception {
943         // Setup NetworkChangeNotifierAutoDetect
944         final TestNetworkChangeNotifierAutoDetectObserver observer =
945                 new TestNetworkChangeNotifierAutoDetectObserver();
946         Callable<NetworkChangeNotifierAutoDetect> callable =
947                 new Callable<NetworkChangeNotifierAutoDetect>() {
948                     @Override
949                     public NetworkChangeNotifierAutoDetect call() {
950                         // This call prevents NetworkChangeNotifierAutoDetect from
951                         // registering for events right off the bat. We'll delay this
952                         // until our MockConnectivityManagerDelegate is first installed
953                         // to prevent inadvertent communication with the real
954                         // ConnectivityManager.
955                         setApplicationHasVisibleActivities(false);
956                         return new NetworkChangeNotifierAutoDetect(
957                                 observer, new RegistrationPolicyApplicationStatus());
958                     }
959                 };
960         FutureTask<NetworkChangeNotifierAutoDetect> task = new FutureTask<>(callable);
961         ThreadUtils.postOnUiThread(task);
962         NetworkChangeNotifierAutoDetect ncn = task.get();
963 
964         // Insert mock ConnectivityDelegate
965         mConnectivityDelegate = new MockConnectivityManagerDelegate();
966         ncn.setConnectivityManagerDelegateForTests(mConnectivityDelegate);
967         // Now that mock ConnectivityDelegate is inserted, pretend app is foregrounded
968         // so NetworkChangeNotifierAutoDetect will register its NetworkCallback.
969         Assert.assertFalse(ncn.isReceiverRegisteredForTesting());
970 
971         RegistrationPolicyApplicationStatus policy =
972                 (RegistrationPolicyApplicationStatus) ncn.getRegistrationPolicy();
973         triggerApplicationStateChange(policy, ApplicationState.HAS_RUNNING_ACTIVITIES);
974         Assert.assertTrue(ncn.isReceiverRegisteredForTesting());
975 
976         // Find NetworkChangeNotifierAutoDetect's NetworkCallback, which should have been registered
977         // with mConnectivityDelegate.
978         NetworkCallback networkCallback = mConnectivityDelegate.getLastRegisteredNetworkCallback();
979         Assert.assertNotNull(networkCallback);
980 
981         // First thing we'll receive is a purge to initialize any network lists.
982         observer.assertLastChange(ChangeType.PURGE_LIST, NetId.INVALID);
983 
984         // Test connected signal is passed along.
985         mConnectivityDelegate.addNetwork(100, TRANSPORT_WIFI, false);
986         observer.assertLastChange(ChangeType.CONNECT, 100);
987 
988         // Test soon-to-be-disconnected signal is passed along.
989         networkCallback.onLosing(Helper.netIdToNetwork(100), 30);
990         observer.assertLastChange(ChangeType.SOON_TO_DISCONNECT, 100);
991 
992         // Test connected signal is passed along.
993         mConnectivityDelegate.removeNetwork(100);
994         observer.assertLastChange(ChangeType.DISCONNECT, 100);
995 
996         // Simulate app backgrounding then foregrounding.
997         Assert.assertTrue(ncn.isReceiverRegisteredForTesting());
998         triggerApplicationStateChange(policy, ApplicationState.HAS_PAUSED_ACTIVITIES);
999         Assert.assertFalse(ncn.isReceiverRegisteredForTesting());
1000         triggerApplicationStateChange(policy, ApplicationState.HAS_RUNNING_ACTIVITIES);
1001         Assert.assertTrue(ncn.isReceiverRegisteredForTesting());
1002         // Verify network list purged.
1003         observer.assertLastChange(ChangeType.PURGE_LIST, NetId.INVALID);
1004 
1005         //
1006         // VPN testing
1007         //
1008 
1009         // Add a couple normal networks
1010         mConnectivityDelegate.addNetwork(100, TRANSPORT_WIFI, false);
1011         observer.assertLastChange(ChangeType.CONNECT, 100);
1012         mConnectivityDelegate.addNetwork(101, TRANSPORT_CELLULAR, false);
1013         observer.assertLastChange(ChangeType.CONNECT, 101);
1014 
1015         // Verify inaccessible VPN is ignored
1016         mConnectivityDelegate.addNetwork(102, TRANSPORT_VPN, false);
1017         NetworkChangeNotifierTestUtil.flushUiThreadTaskQueue();
1018         Assert.assertEquals(observer.mChanges.size(), 0);
1019         // The disconnect will be ignored in
1020         // NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkDisconnect() because no
1021         // connect event was witnessed, but it will be sent to {@code observer}
1022         mConnectivityDelegate.removeNetwork(102);
1023         observer.assertLastChange(ChangeType.DISCONNECT, 102);
1024 
1025         // Verify when an accessible VPN connects, all other network disconnect
1026         mConnectivityDelegate.addNetwork(103, TRANSPORT_VPN, true);
1027         NetworkChangeNotifierTestUtil.flushUiThreadTaskQueue();
1028         Assert.assertEquals(2, observer.mChanges.size());
1029         Assert.assertEquals(ChangeType.CONNECT, observer.mChanges.get(0).mChangeType);
1030         Assert.assertEquals(103, observer.mChanges.get(0).mNetId);
1031         Assert.assertEquals(ChangeType.PURGE_LIST, observer.mChanges.get(1).mChangeType);
1032         Assert.assertEquals(103, observer.mChanges.get(1).mNetId);
1033         observer.mChanges.clear();
1034 
1035         // Verify when an accessible VPN disconnects, all other networks reconnect
1036         mConnectivityDelegate.removeNetwork(103);
1037         NetworkChangeNotifierTestUtil.flushUiThreadTaskQueue();
1038         Assert.assertEquals(3, observer.mChanges.size());
1039         Assert.assertEquals(ChangeType.DISCONNECT, observer.mChanges.get(0).mChangeType);
1040         Assert.assertEquals(103, observer.mChanges.get(0).mNetId);
1041         Assert.assertEquals(ChangeType.CONNECT, observer.mChanges.get(1).mChangeType);
1042         Assert.assertEquals(100, observer.mChanges.get(1).mNetId);
1043         Assert.assertEquals(ChangeType.CONNECT, observer.mChanges.get(2).mChangeType);
1044         Assert.assertEquals(101, observer.mChanges.get(2).mNetId);
1045     }
1046 
1047     /**
1048      * Tests that isOnline() returns the correct result.
1049      */
1050     @Test
1051     @UiThreadTest
1052     @MediumTest
1053     @Feature({"Android-AppBase"})
testNetworkChangeNotifierIsOnline()1054     public void testNetworkChangeNotifierIsOnline() {
1055         mReceiver.register();
1056         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
1057         // For any connection type it should return true.
1058         for (int i = ConnectivityManager.TYPE_MOBILE; i < ConnectivityManager.TYPE_VPN; i++) {
1059             mConnectivityDelegate.setActiveNetworkExists(true);
1060             mConnectivityDelegate.setNetworkType(i);
1061             mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), intent);
1062             Assert.assertTrue(NetworkChangeNotifier.isOnline());
1063         }
1064         mConnectivityDelegate.setActiveNetworkExists(false);
1065         mReceiver.onReceive(InstrumentationRegistry.getTargetContext(), intent);
1066         Assert.assertFalse(NetworkChangeNotifier.isOnline());
1067     }
1068 
1069     /**
1070      * Regression test for crbug.com/805424 where ConnectivityManagerDelegate.vpnAccessible() was
1071      * found to leak.
1072      */
1073     @Test
1074     @MediumTest
1075     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) // android.net.Network available in L+.
testVpnAccessibleDoesNotLeak()1076     public void testVpnAccessibleDoesNotLeak() {
1077         ConnectivityManagerDelegate connectivityManagerDelegate = new ConnectivityManagerDelegate(
1078                 InstrumentationRegistry.getInstrumentation().getTargetContext());
1079         StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
1080         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
1081                                        .detectLeakedClosableObjects()
1082                                        .penaltyDeath()
1083                                        .penaltyLog()
1084                                        .build());
1085         try {
1086             // Test non-existent Network (NetIds only go to 65535).
1087             connectivityManagerDelegate.vpnAccessible(Helper.netIdToNetwork(65537));
1088             // Test existing Networks.
1089             for (Network network : connectivityManagerDelegate.getAllNetworksUnfiltered()) {
1090                 connectivityManagerDelegate.vpnAccessible(network);
1091             }
1092 
1093             // Run GC and finalizers a few times to pick up leaked closeables
1094             for (int i = 0; i < 10; i++) {
1095                 System.gc();
1096                 System.runFinalization();
1097             }
1098             System.gc();
1099             System.runFinalization();
1100         } finally {
1101             StrictMode.setVmPolicy(oldPolicy);
1102         }
1103     }
1104 
1105     /**
1106      * Regression test for crbug.com/946531 where ConnectivityManagerDelegate.vpnAccessible()
1107      * triggered StrictMode's untagged socket prohibition.
1108      */
1109     @Test
1110     @MediumTest
1111     @MinAndroidSdkLevel(Build.VERSION_CODES.O) // detectUntaggedSockets added in Oreo.
testVpnAccessibleDoesNotCreateUntaggedSockets()1112     public void testVpnAccessibleDoesNotCreateUntaggedSockets() {
1113         ConnectivityManagerDelegate connectivityManagerDelegate = new ConnectivityManagerDelegate(
1114                 InstrumentationRegistry.getInstrumentation().getTargetContext());
1115         StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
1116         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
1117                                        .detectUntaggedSockets()
1118                                        .penaltyDeath()
1119                                        .penaltyLog()
1120                                        .build());
1121         try {
1122             // Test non-existent Network (NetIds only go to 65535).
1123             connectivityManagerDelegate.vpnAccessible(Helper.netIdToNetwork(65537));
1124             // Test existing Networks.
1125             for (Network network : connectivityManagerDelegate.getAllNetworksUnfiltered()) {
1126                 connectivityManagerDelegate.vpnAccessible(network);
1127             }
1128         } finally {
1129             StrictMode.setVmPolicy(oldPolicy);
1130         }
1131     }
1132 }
1133