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