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