• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import static android.net.ConnectivityManager.TYPE_NONE;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
24 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
25 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
26 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
27 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
28 import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
29 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
30 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
31 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
32 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
33 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
34 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
35 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
36 import static android.net.NetworkRequest.Type.REQUEST;
37 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
38 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
39 
40 import static com.android.testutils.MiscAsserts.assertThrows;
41 
42 import static org.junit.Assert.assertFalse;
43 import static org.junit.Assert.assertNotNull;
44 import static org.junit.Assert.assertNull;
45 import static org.junit.Assert.assertTrue;
46 import static org.junit.Assert.fail;
47 import static org.mockito.ArgumentMatchers.anyBoolean;
48 import static org.mockito.ArgumentMatchers.eq;
49 import static org.mockito.ArgumentMatchers.nullable;
50 import static org.mockito.Mockito.CALLS_REAL_METHODS;
51 import static org.mockito.Mockito.after;
52 import static org.mockito.Mockito.any;
53 import static org.mockito.Mockito.anyInt;
54 import static org.mockito.Mockito.mock;
55 import static org.mockito.Mockito.never;
56 import static org.mockito.Mockito.reset;
57 import static org.mockito.Mockito.timeout;
58 import static org.mockito.Mockito.times;
59 import static org.mockito.Mockito.verify;
60 import static org.mockito.Mockito.when;
61 
62 import android.app.PendingIntent;
63 import android.content.Context;
64 import android.content.pm.ApplicationInfo;
65 import android.net.ConnectivityManager.NetworkCallback;
66 import android.os.Build.VERSION_CODES;
67 import android.os.Bundle;
68 import android.os.Handler;
69 import android.os.Looper;
70 import android.os.Message;
71 import android.os.Messenger;
72 import android.os.Process;
73 
74 import androidx.test.filters.SmallTest;
75 
76 import com.android.internal.util.test.BroadcastInterceptingContext;
77 import com.android.testutils.DevSdkIgnoreRule;
78 import com.android.testutils.DevSdkIgnoreRunner;
79 
80 import org.junit.Before;
81 import org.junit.Test;
82 import org.junit.runner.RunWith;
83 import org.mockito.ArgumentCaptor;
84 import org.mockito.Mock;
85 import org.mockito.MockitoAnnotations;
86 
87 import java.lang.ref.WeakReference;
88 
89 @RunWith(DevSdkIgnoreRunner.class)
90 @SmallTest
91 @DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
92 public class ConnectivityManagerTest {
93     private static final int TIMEOUT_MS = 30_000;
94     private static final int SHORT_TIMEOUT_MS = 150;
95 
96     @Mock Context mCtx;
97     @Mock IConnectivityManager mService;
98 
99     @Before
setUp()100     public void setUp() {
101         MockitoAnnotations.initMocks(this);
102     }
103 
verifyNetworkCapabilities( int legacyType, int transportType, int... capabilities)104     static NetworkCapabilities verifyNetworkCapabilities(
105             int legacyType, int transportType, int... capabilities) {
106         final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
107         assertNotNull(nc);
108         assertTrue(nc.hasTransport(transportType));
109         for (int capability : capabilities) {
110             assertTrue(nc.hasCapability(capability));
111         }
112 
113         return nc;
114     }
115 
verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType)116     static void verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType) {
117         verifyNetworkCapabilities(
118                 legacyType,
119                 transportType,
120                 NET_CAPABILITY_INTERNET,
121                 NET_CAPABILITY_NOT_RESTRICTED,
122                 NET_CAPABILITY_NOT_VPN,
123                 NET_CAPABILITY_TRUSTED);
124     }
125 
verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability)126     static void verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability) {
127         final NetworkCapabilities nc = verifyNetworkCapabilities(
128                 legacyType,
129                 TRANSPORT_CELLULAR,
130                 capability,
131                 NET_CAPABILITY_NOT_VPN,
132                 NET_CAPABILITY_TRUSTED);
133 
134         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
135         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
136     }
137 
138     @Test
testNetworkCapabilitiesForTypeMobile()139     public void testNetworkCapabilitiesForTypeMobile() {
140         verifyUnrestrictedNetworkCapabilities(
141                 ConnectivityManager.TYPE_MOBILE, TRANSPORT_CELLULAR);
142     }
143 
144     @Test
testNetworkCapabilitiesForTypeMobileCbs()145     public void testNetworkCapabilitiesForTypeMobileCbs() {
146         verifyRestrictedMobileNetworkCapabilities(
147                 ConnectivityManager.TYPE_MOBILE_CBS, NET_CAPABILITY_CBS);
148     }
149 
150     @Test
testNetworkCapabilitiesForTypeMobileDun()151     public void testNetworkCapabilitiesForTypeMobileDun() {
152         verifyRestrictedMobileNetworkCapabilities(
153                 ConnectivityManager.TYPE_MOBILE_DUN, NET_CAPABILITY_DUN);
154     }
155 
156     @Test
testNetworkCapabilitiesForTypeMobileFota()157     public void testNetworkCapabilitiesForTypeMobileFota() {
158         verifyRestrictedMobileNetworkCapabilities(
159                 ConnectivityManager.TYPE_MOBILE_FOTA, NET_CAPABILITY_FOTA);
160     }
161 
162     @Test
testNetworkCapabilitiesForTypeMobileHipri()163     public void testNetworkCapabilitiesForTypeMobileHipri() {
164         verifyUnrestrictedNetworkCapabilities(
165                 ConnectivityManager.TYPE_MOBILE_HIPRI, TRANSPORT_CELLULAR);
166     }
167 
168     @Test
testNetworkCapabilitiesForTypeMobileIms()169     public void testNetworkCapabilitiesForTypeMobileIms() {
170         verifyRestrictedMobileNetworkCapabilities(
171                 ConnectivityManager.TYPE_MOBILE_IMS, NET_CAPABILITY_IMS);
172     }
173 
174     @Test
testNetworkCapabilitiesForTypeMobileMms()175     public void testNetworkCapabilitiesForTypeMobileMms() {
176         final NetworkCapabilities nc = verifyNetworkCapabilities(
177                 ConnectivityManager.TYPE_MOBILE_MMS,
178                 TRANSPORT_CELLULAR,
179                 NET_CAPABILITY_MMS,
180                 NET_CAPABILITY_NOT_VPN,
181                 NET_CAPABILITY_TRUSTED);
182 
183         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
184     }
185 
186     @Test
testNetworkCapabilitiesForTypeMobileSupl()187     public void testNetworkCapabilitiesForTypeMobileSupl() {
188         final NetworkCapabilities nc = verifyNetworkCapabilities(
189                 ConnectivityManager.TYPE_MOBILE_SUPL,
190                 TRANSPORT_CELLULAR,
191                 NET_CAPABILITY_SUPL,
192                 NET_CAPABILITY_NOT_VPN,
193                 NET_CAPABILITY_TRUSTED);
194 
195         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
196     }
197 
198     @Test
testNetworkCapabilitiesForTypeWifi()199     public void testNetworkCapabilitiesForTypeWifi() {
200         verifyUnrestrictedNetworkCapabilities(
201                 ConnectivityManager.TYPE_WIFI, TRANSPORT_WIFI);
202     }
203 
204     @Test
testNetworkCapabilitiesForTypeWifiP2p()205     public void testNetworkCapabilitiesForTypeWifiP2p() {
206         final NetworkCapabilities nc = verifyNetworkCapabilities(
207                 ConnectivityManager.TYPE_WIFI_P2P,
208                 TRANSPORT_WIFI,
209                 NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_VPN,
210                 NET_CAPABILITY_TRUSTED, NET_CAPABILITY_WIFI_P2P);
211 
212         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
213     }
214 
215     @Test
testNetworkCapabilitiesForTypeBluetooth()216     public void testNetworkCapabilitiesForTypeBluetooth() {
217         verifyUnrestrictedNetworkCapabilities(
218                 ConnectivityManager.TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH);
219     }
220 
221     @Test
testNetworkCapabilitiesForTypeEthernet()222     public void testNetworkCapabilitiesForTypeEthernet() {
223         verifyUnrestrictedNetworkCapabilities(
224                 ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET);
225     }
226 
227     @Test
testCallbackRelease()228     public void testCallbackRelease() throws Exception {
229         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
230         NetworkRequest request = makeRequest(1);
231         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class,
232                 CALLS_REAL_METHODS);
233         Handler handler = new Handler(Looper.getMainLooper());
234         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
235 
236         // register callback
237         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
238                 anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request);
239         manager.requestNetwork(request, callback, handler);
240 
241         // callback triggers
242         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
243         verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
244                 any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
245 
246         // unregister callback
247         manager.unregisterNetworkCallback(callback);
248         verify(mService, times(1)).releaseNetworkRequest(request);
249 
250         // callback does not trigger anymore.
251         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING));
252         verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
253     }
254 
255     @Test
testCallbackRecycling()256     public void testCallbackRecycling() throws Exception {
257         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
258         NetworkRequest req1 = makeRequest(1);
259         NetworkRequest req2 = makeRequest(2);
260         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class,
261                 CALLS_REAL_METHODS);
262         Handler handler = new Handler(Looper.getMainLooper());
263         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
264 
265         // register callback
266         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
267                 anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1);
268         manager.requestNetwork(req1, callback, handler);
269 
270         // callback triggers
271         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
272         verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
273                 any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
274 
275         // unregister callback
276         manager.unregisterNetworkCallback(callback);
277         verify(mService, times(1)).releaseNetworkRequest(req1);
278 
279         // callback does not trigger anymore.
280         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING));
281         verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
282 
283         // callback can be registered again
284         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
285                 anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2);
286         manager.requestNetwork(req2, callback, handler);
287 
288         // callback triggers
289         captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST));
290         verify(callback, timeout(TIMEOUT_MS).times(1)).onLost(any());
291 
292         // unregister callback
293         manager.unregisterNetworkCallback(callback);
294         verify(mService, times(1)).releaseNetworkRequest(req2);
295     }
296 
297     // TODO: turn on this test when request  callback 1:1 mapping is enforced
298     //@Test
noDoubleCallbackRegistration()299     private void noDoubleCallbackRegistration() throws Exception {
300         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
301         NetworkRequest request = makeRequest(1);
302         NetworkCallback callback = new ConnectivityManager.NetworkCallback();
303         ApplicationInfo info = new ApplicationInfo();
304         // TODO: update version when starting to enforce 1:1 mapping
305         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
306 
307         when(mCtx.getApplicationInfo()).thenReturn(info);
308         when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(),
309                 anyInt(), any(), nullable(String.class))).thenReturn(request);
310 
311         Handler handler = new Handler(Looper.getMainLooper());
312         manager.requestNetwork(request, callback, handler);
313 
314         // callback is already registered, reregistration should fail.
315         Class<IllegalArgumentException> wantException = IllegalArgumentException.class;
316         expectThrowable(() -> manager.requestNetwork(request, callback), wantException);
317 
318         manager.unregisterNetworkCallback(callback);
319         verify(mService, times(1)).releaseNetworkRequest(request);
320 
321         // unregistering the callback should make it registrable again.
322         manager.requestNetwork(request, callback);
323     }
324 
325     @Test
testDefaultNetworkActiveListener()326     public void testDefaultNetworkActiveListener() throws Exception {
327         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
328         final ConnectivityManager.OnNetworkActiveListener listener =
329                 mock(ConnectivityManager.OnNetworkActiveListener.class);
330         assertThrows(IllegalArgumentException.class,
331                 () -> manager.removeDefaultNetworkActiveListener(listener));
332         manager.addDefaultNetworkActiveListener(listener);
333         verify(mService, times(1)).registerNetworkActivityListener(any());
334         manager.removeDefaultNetworkActiveListener(listener);
335         verify(mService, times(1)).unregisterNetworkActivityListener(any());
336         assertThrows(IllegalArgumentException.class,
337                 () -> manager.removeDefaultNetworkActiveListener(listener));
338     }
339 
340     @Test
testArgumentValidation()341     public void testArgumentValidation() throws Exception {
342         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
343 
344         NetworkRequest request = mock(NetworkRequest.class);
345         NetworkCallback callback = mock(NetworkCallback.class);
346         Handler handler = mock(Handler.class);
347         NetworkCallback nullCallback = null;
348         PendingIntent nullIntent = null;
349 
350         mustFail(() -> manager.requestNetwork(null, callback));
351         mustFail(() -> manager.requestNetwork(request, nullCallback));
352         mustFail(() -> manager.requestNetwork(request, callback, null));
353         mustFail(() -> manager.requestNetwork(request, callback, -1));
354         mustFail(() -> manager.requestNetwork(request, nullIntent));
355 
356         mustFail(() -> manager.requestBackgroundNetwork(null, callback, handler));
357         mustFail(() -> manager.requestBackgroundNetwork(request, null, handler));
358         mustFail(() -> manager.requestBackgroundNetwork(request, callback, null));
359 
360         mustFail(() -> manager.registerNetworkCallback(null, callback, handler));
361         mustFail(() -> manager.registerNetworkCallback(request, null, handler));
362         mustFail(() -> manager.registerNetworkCallback(request, callback, null));
363         mustFail(() -> manager.registerNetworkCallback(request, nullIntent));
364 
365         mustFail(() -> manager.registerDefaultNetworkCallback(null, handler));
366         mustFail(() -> manager.registerDefaultNetworkCallback(callback, null));
367 
368         mustFail(() -> manager.registerSystemDefaultNetworkCallback(null, handler));
369         mustFail(() -> manager.registerSystemDefaultNetworkCallback(callback, null));
370 
371         mustFail(() -> manager.registerBestMatchingNetworkCallback(null, callback, handler));
372         mustFail(() -> manager.registerBestMatchingNetworkCallback(request, null, handler));
373         mustFail(() -> manager.registerBestMatchingNetworkCallback(request, callback, null));
374 
375         mustFail(() -> manager.unregisterNetworkCallback(nullCallback));
376         mustFail(() -> manager.unregisterNetworkCallback(nullIntent));
377         mustFail(() -> manager.releaseNetworkRequest(nullIntent));
378     }
379 
mustFail(Runnable fn)380     static void mustFail(Runnable fn) {
381         try {
382             fn.run();
383             fail();
384         } catch (Exception expected) {
385         }
386     }
387 
388     @Test
testRequestType()389     public void testRequestType() throws Exception {
390         final String testPkgName = "MyPackage";
391         final String testAttributionTag = "MyTag";
392         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
393         when(mCtx.getOpPackageName()).thenReturn(testPkgName);
394         when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
395         final NetworkRequest request = makeRequest(1);
396         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
397 
398         manager.requestNetwork(request, callback);
399         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
400                 eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
401                 eq(testPkgName), eq(testAttributionTag));
402         reset(mService);
403 
404         // Verify that register network callback does not calls requestNetwork at all.
405         manager.registerNetworkCallback(request, callback);
406         verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(),
407                 anyInt(), anyInt(), any(), any());
408         verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
409                 eq(testPkgName), eq(testAttributionTag));
410         reset(mService);
411 
412         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
413 
414         manager.registerDefaultNetworkCallback(callback);
415         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
416                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
417                 eq(testPkgName), eq(testAttributionTag));
418         reset(mService);
419 
420         manager.registerDefaultNetworkCallbackForUid(42, callback, handler);
421         verify(mService).requestNetwork(eq(42), eq(null),
422                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
423                 eq(testPkgName), eq(testAttributionTag));
424 
425         manager.requestBackgroundNetwork(request, callback, handler);
426         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
427                 eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
428                 eq(testPkgName), eq(testAttributionTag));
429         reset(mService);
430 
431         manager.registerSystemDefaultNetworkCallback(callback, handler);
432         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
433                 eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
434                 eq(testPkgName), eq(testAttributionTag));
435         reset(mService);
436     }
437 
makeMessage(NetworkRequest req, int messageType)438     static Message makeMessage(NetworkRequest req, int messageType) {
439         Bundle bundle = new Bundle();
440         bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
441         // Pass default objects as we don't care which get passed here
442         bundle.putParcelable(Network.class.getSimpleName(), new Network(1));
443         bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities());
444         bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties());
445         Message msg = Message.obtain();
446         msg.what = messageType;
447         msg.setData(bundle);
448         return msg;
449     }
450 
makeRequest(int requestId)451     static NetworkRequest makeRequest(int requestId) {
452         NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
453         return new NetworkRequest(request.networkCapabilities, ConnectivityManager.TYPE_NONE,
454                 requestId, NetworkRequest.Type.NONE);
455     }
456 
expectThrowable(Runnable block, Class<? extends Throwable> throwableType)457     static void expectThrowable(Runnable block, Class<? extends Throwable> throwableType) {
458         try {
459             block.run();
460         } catch (Throwable t) {
461             if (t.getClass().equals(throwableType)) {
462                 return;
463             }
464             fail("expected exception of type " + throwableType + ", but was " + t.getClass());
465         }
466         fail("expected exception of type " + throwableType);
467     }
468 
469     private static class MockContext extends BroadcastInterceptingContext {
MockContext(Context base)470         MockContext(Context base) {
471             super(base);
472         }
473 
474         @Override
getApplicationContext()475         public Context getApplicationContext() {
476             return mock(Context.class);
477         }
478     }
479 
makeConnectivityManagerAndReturnContext()480     private WeakReference<Context> makeConnectivityManagerAndReturnContext() {
481         // Mockito may have an internal reference to the mock, creating MockContext for testing.
482         final Context c = new MockContext(mock(Context.class));
483 
484         new ConnectivityManager(c, mService);
485 
486         return new WeakReference<>(c);
487     }
488 
forceGC()489     private void forceGC() {
490         // First GC ensures that objects are collected for finalization, then second GC ensures
491         // they're garbage-collected after being finalized.
492         System.gc();
493         System.runFinalization();
494         System.gc();
495     }
496 
497     @Test
testConnectivityManagerDoesNotLeakContext()498     public void testConnectivityManagerDoesNotLeakContext() throws Exception {
499         final WeakReference<Context> ref = makeConnectivityManagerAndReturnContext();
500 
501         final int attempts = 600;
502         final long waitIntervalMs = 50;
503         for (int i = 0; i < attempts; i++) {
504             forceGC();
505             if (ref.get() == null) break;
506 
507             Thread.sleep(waitIntervalMs);
508         }
509 
510         assertNull("ConnectivityManager weak reference still not null after " + attempts
511                     + " attempts", ref.get());
512     }
513 }
514