• 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.doReturn;
55 import static org.mockito.Mockito.inOrder;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.never;
58 import static org.mockito.Mockito.reset;
59 import static org.mockito.Mockito.timeout;
60 import static org.mockito.Mockito.times;
61 import static org.mockito.Mockito.verify;
62 import static org.mockito.Mockito.when;
63 
64 import android.app.PendingIntent;
65 import android.content.Context;
66 import android.content.pm.ApplicationInfo;
67 import android.net.ConnectivityManager.NetworkCallback;
68 import android.os.Build.VERSION_CODES;
69 import android.os.Bundle;
70 import android.os.Handler;
71 import android.os.Looper;
72 import android.os.Message;
73 import android.os.Messenger;
74 import android.os.Process;
75 
76 import androidx.annotation.NonNull;
77 import androidx.test.filters.SmallTest;
78 
79 import com.android.internal.util.test.BroadcastInterceptingContext;
80 import com.android.testutils.DevSdkIgnoreRule;
81 import com.android.testutils.DevSdkIgnoreRunner;
82 
83 import org.junit.Before;
84 import org.junit.Rule;
85 import org.junit.Test;
86 import org.junit.runner.RunWith;
87 import org.mockito.ArgumentCaptor;
88 import org.mockito.InOrder;
89 import org.mockito.Mock;
90 import org.mockito.MockitoAnnotations;
91 
92 import java.lang.ref.WeakReference;
93 
94 @RunWith(DevSdkIgnoreRunner.class)
95 @SmallTest
96 @DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
97 public class ConnectivityManagerTest {
98     @Rule
99     public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
100     private static final int TIMEOUT_MS = 30_000;
101     private static final int SHORT_TIMEOUT_MS = 150;
102 
103     @Mock Context mCtx;
104     @Mock IConnectivityManager mService;
105     @Mock NetworkPolicyManager mNpm;
106 
107     @Before
setUp()108     public void setUp() {
109         MockitoAnnotations.initMocks(this);
110     }
111 
verifyNetworkCapabilities( int legacyType, int transportType, int... capabilities)112     static NetworkCapabilities verifyNetworkCapabilities(
113             int legacyType, int transportType, int... capabilities) {
114         final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
115         assertNotNull(nc);
116         assertTrue(nc.hasTransport(transportType));
117         for (int capability : capabilities) {
118             assertTrue(nc.hasCapability(capability));
119         }
120 
121         return nc;
122     }
123 
verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType)124     static void verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType) {
125         verifyNetworkCapabilities(
126                 legacyType,
127                 transportType,
128                 NET_CAPABILITY_INTERNET,
129                 NET_CAPABILITY_NOT_RESTRICTED,
130                 NET_CAPABILITY_NOT_VPN,
131                 NET_CAPABILITY_TRUSTED);
132     }
133 
verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability)134     static void verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability) {
135         final NetworkCapabilities nc = verifyNetworkCapabilities(
136                 legacyType,
137                 TRANSPORT_CELLULAR,
138                 capability,
139                 NET_CAPABILITY_NOT_VPN,
140                 NET_CAPABILITY_TRUSTED);
141 
142         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
143         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
144     }
145 
146     @Test
testNetworkCapabilitiesForTypeMobile()147     public void testNetworkCapabilitiesForTypeMobile() {
148         verifyUnrestrictedNetworkCapabilities(
149                 ConnectivityManager.TYPE_MOBILE, TRANSPORT_CELLULAR);
150     }
151 
152     @Test
testNetworkCapabilitiesForTypeMobileCbs()153     public void testNetworkCapabilitiesForTypeMobileCbs() {
154         verifyRestrictedMobileNetworkCapabilities(
155                 ConnectivityManager.TYPE_MOBILE_CBS, NET_CAPABILITY_CBS);
156     }
157 
158     @Test
testNetworkCapabilitiesForTypeMobileDun()159     public void testNetworkCapabilitiesForTypeMobileDun() {
160         verifyRestrictedMobileNetworkCapabilities(
161                 ConnectivityManager.TYPE_MOBILE_DUN, NET_CAPABILITY_DUN);
162     }
163 
164     @Test
testNetworkCapabilitiesForTypeMobileFota()165     public void testNetworkCapabilitiesForTypeMobileFota() {
166         verifyRestrictedMobileNetworkCapabilities(
167                 ConnectivityManager.TYPE_MOBILE_FOTA, NET_CAPABILITY_FOTA);
168     }
169 
170     @Test
testNetworkCapabilitiesForTypeMobileHipri()171     public void testNetworkCapabilitiesForTypeMobileHipri() {
172         verifyUnrestrictedNetworkCapabilities(
173                 ConnectivityManager.TYPE_MOBILE_HIPRI, TRANSPORT_CELLULAR);
174     }
175 
176     @Test
testNetworkCapabilitiesForTypeMobileIms()177     public void testNetworkCapabilitiesForTypeMobileIms() {
178         verifyRestrictedMobileNetworkCapabilities(
179                 ConnectivityManager.TYPE_MOBILE_IMS, NET_CAPABILITY_IMS);
180     }
181 
182     @Test
testNetworkCapabilitiesForTypeMobileMms()183     public void testNetworkCapabilitiesForTypeMobileMms() {
184         final NetworkCapabilities nc = verifyNetworkCapabilities(
185                 ConnectivityManager.TYPE_MOBILE_MMS,
186                 TRANSPORT_CELLULAR,
187                 NET_CAPABILITY_MMS,
188                 NET_CAPABILITY_NOT_VPN,
189                 NET_CAPABILITY_TRUSTED);
190 
191         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
192     }
193 
194     @Test
testNetworkCapabilitiesForTypeMobileSupl()195     public void testNetworkCapabilitiesForTypeMobileSupl() {
196         final NetworkCapabilities nc = verifyNetworkCapabilities(
197                 ConnectivityManager.TYPE_MOBILE_SUPL,
198                 TRANSPORT_CELLULAR,
199                 NET_CAPABILITY_SUPL,
200                 NET_CAPABILITY_NOT_VPN,
201                 NET_CAPABILITY_TRUSTED);
202 
203         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
204     }
205 
206     @Test
testNetworkCapabilitiesForTypeWifi()207     public void testNetworkCapabilitiesForTypeWifi() {
208         verifyUnrestrictedNetworkCapabilities(
209                 ConnectivityManager.TYPE_WIFI, TRANSPORT_WIFI);
210     }
211 
212     @Test
testNetworkCapabilitiesForTypeWifiP2p()213     public void testNetworkCapabilitiesForTypeWifiP2p() {
214         final NetworkCapabilities nc = verifyNetworkCapabilities(
215                 ConnectivityManager.TYPE_WIFI_P2P,
216                 TRANSPORT_WIFI,
217                 NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_VPN,
218                 NET_CAPABILITY_TRUSTED, NET_CAPABILITY_WIFI_P2P);
219 
220         assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET));
221     }
222 
223     @Test
testNetworkCapabilitiesForTypeBluetooth()224     public void testNetworkCapabilitiesForTypeBluetooth() {
225         verifyUnrestrictedNetworkCapabilities(
226                 ConnectivityManager.TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH);
227     }
228 
229     @Test
testNetworkCapabilitiesForTypeEthernet()230     public void testNetworkCapabilitiesForTypeEthernet() {
231         verifyUnrestrictedNetworkCapabilities(
232                 ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET);
233     }
234 
235     @Test
testCallbackRelease()236     public void testCallbackRelease() throws Exception {
237         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
238         NetworkRequest request = makeRequest(1);
239         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class,
240                 CALLS_REAL_METHODS);
241         Handler handler = new Handler(Looper.getMainLooper());
242         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
243 
244         // register callback
245         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
246                 anyInt(), anyInt(), any(), nullable(String.class), anyInt())).thenReturn(request);
247         manager.requestNetwork(request, callback, handler);
248 
249         // callback triggers
250         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
251         verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
252                 any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
253 
254         // unregister callback
255         manager.unregisterNetworkCallback(callback);
256         verify(mService, times(1)).releaseNetworkRequest(request);
257 
258         // callback does not trigger anymore.
259         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING));
260         verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
261     }
262 
263     @Test
testCallbackRecycling()264     public void testCallbackRecycling() throws Exception {
265         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
266         NetworkRequest req1 = makeRequest(1);
267         NetworkRequest req2 = makeRequest(2);
268         NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class,
269                 CALLS_REAL_METHODS);
270         Handler handler = new Handler(Looper.getMainLooper());
271         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
272 
273         // register callback
274         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
275                 anyInt(), anyInt(), any(), nullable(String.class), anyInt())).thenReturn(req1);
276         manager.requestNetwork(req1, callback, handler);
277 
278         // callback triggers
279         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
280         verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
281                 any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
282 
283         // unregister callback
284         manager.unregisterNetworkCallback(callback);
285         verify(mService, times(1)).releaseNetworkRequest(req1);
286 
287         // callback does not trigger anymore.
288         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING));
289         verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
290 
291         // callback can be registered again
292         when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
293                 anyInt(), anyInt(), any(), nullable(String.class), anyInt())).thenReturn(req2);
294         manager.requestNetwork(req2, callback, handler);
295 
296         // callback triggers
297         captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST));
298         verify(callback, timeout(TIMEOUT_MS).times(1)).onLost(any());
299 
300         // unregister callback
301         manager.unregisterNetworkCallback(callback);
302         verify(mService, times(1)).releaseNetworkRequest(req2);
303     }
304 
305     // TODO: turn on this test when request  callback 1:1 mapping is enforced
306     //@Test
noDoubleCallbackRegistration()307     private void noDoubleCallbackRegistration() throws Exception {
308         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
309         NetworkRequest request = makeRequest(1);
310         NetworkCallback callback = new ConnectivityManager.NetworkCallback();
311         ApplicationInfo info = new ApplicationInfo();
312         // TODO: update version when starting to enforce 1:1 mapping
313         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
314 
315         when(mCtx.getApplicationInfo()).thenReturn(info);
316         when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(),
317                 anyInt(), any(), nullable(String.class), anyInt())).thenReturn(request);
318 
319         Handler handler = new Handler(Looper.getMainLooper());
320         manager.requestNetwork(request, callback, handler);
321 
322         // callback is already registered, reregistration should fail.
323         Class<IllegalArgumentException> wantException = IllegalArgumentException.class;
324         expectThrowable(() -> manager.requestNetwork(request, callback), wantException);
325 
326         manager.unregisterNetworkCallback(callback);
327         verify(mService, times(1)).releaseNetworkRequest(request);
328 
329         // unregistering the callback should make it registrable again.
330         manager.requestNetwork(request, callback);
331     }
332 
333     @Test
testDefaultNetworkActiveListener()334     public void testDefaultNetworkActiveListener() throws Exception {
335         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
336         final ConnectivityManager.OnNetworkActiveListener listener =
337                 mock(ConnectivityManager.OnNetworkActiveListener.class);
338         assertThrows(IllegalArgumentException.class,
339                 () -> manager.removeDefaultNetworkActiveListener(listener));
340         manager.addDefaultNetworkActiveListener(listener);
341         verify(mService, times(1)).registerNetworkActivityListener(any());
342         manager.removeDefaultNetworkActiveListener(listener);
343         verify(mService, times(1)).unregisterNetworkActivityListener(any());
344         assertThrows(IllegalArgumentException.class,
345                 () -> manager.removeDefaultNetworkActiveListener(listener));
346     }
347 
348     @Test
testArgumentValidation()349     public void testArgumentValidation() throws Exception {
350         ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
351 
352         NetworkRequest request = mock(NetworkRequest.class);
353         NetworkCallback callback = mock(NetworkCallback.class);
354         Handler handler = mock(Handler.class);
355         NetworkCallback nullCallback = null;
356         PendingIntent nullIntent = null;
357 
358         mustFail(() -> manager.requestNetwork(null, callback));
359         mustFail(() -> manager.requestNetwork(request, nullCallback));
360         mustFail(() -> manager.requestNetwork(request, callback, null));
361         mustFail(() -> manager.requestNetwork(request, callback, -1));
362         mustFail(() -> manager.requestNetwork(request, nullIntent));
363 
364         mustFail(() -> manager.requestBackgroundNetwork(null, callback, handler));
365         mustFail(() -> manager.requestBackgroundNetwork(request, null, handler));
366         mustFail(() -> manager.requestBackgroundNetwork(request, callback, null));
367 
368         mustFail(() -> manager.registerNetworkCallback(null, callback, handler));
369         mustFail(() -> manager.registerNetworkCallback(request, null, handler));
370         mustFail(() -> manager.registerNetworkCallback(request, callback, null));
371         mustFail(() -> manager.registerNetworkCallback(request, nullIntent));
372 
373         mustFail(() -> manager.registerDefaultNetworkCallback(null, handler));
374         mustFail(() -> manager.registerDefaultNetworkCallback(callback, null));
375 
376         mustFail(() -> manager.registerSystemDefaultNetworkCallback(null, handler));
377         mustFail(() -> manager.registerSystemDefaultNetworkCallback(callback, null));
378 
379         mustFail(() -> manager.registerBestMatchingNetworkCallback(null, callback, handler));
380         mustFail(() -> manager.registerBestMatchingNetworkCallback(request, null, handler));
381         mustFail(() -> manager.registerBestMatchingNetworkCallback(request, callback, null));
382 
383         mustFail(() -> manager.unregisterNetworkCallback(nullCallback));
384         mustFail(() -> manager.unregisterNetworkCallback(nullIntent));
385         mustFail(() -> manager.releaseNetworkRequest(nullIntent));
386     }
387 
mustFail(Runnable fn)388     static void mustFail(Runnable fn) {
389         try {
390             fn.run();
391             fail();
392         } catch (Exception expected) {
393         }
394     }
395 
396     @Test
testRequestType()397     public void testRequestType() throws Exception {
398         final String testPkgName = "MyPackage";
399         final String testAttributionTag = "MyTag";
400         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
401         when(mCtx.getOpPackageName()).thenReturn(testPkgName);
402         when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
403         final NetworkRequest request = makeRequest(1);
404         final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
405 
406         manager.requestNetwork(request, callback);
407         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
408                 eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
409                 eq(testPkgName), eq(testAttributionTag), anyInt());
410         reset(mService);
411 
412         // Verify that register network callback does not calls requestNetwork at all.
413         manager.registerNetworkCallback(request, callback);
414         verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(),
415                 anyInt(), anyInt(), any(), any(), anyInt());
416         verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
417                 eq(testPkgName), eq(testAttributionTag), anyInt());
418         reset(mService);
419 
420         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
421 
422         manager.registerDefaultNetworkCallback(callback);
423         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
424                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
425                 eq(testPkgName), eq(testAttributionTag), anyInt());
426         reset(mService);
427 
428         manager.registerDefaultNetworkCallbackForUid(42, callback, handler);
429         verify(mService).requestNetwork(eq(42), eq(null),
430                 eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
431                 eq(testPkgName), eq(testAttributionTag), anyInt());
432 
433         manager.requestBackgroundNetwork(request, callback, handler);
434         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
435                 eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
436                 eq(testPkgName), eq(testAttributionTag), anyInt());
437         reset(mService);
438 
439         manager.registerSystemDefaultNetworkCallback(callback, handler);
440         verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null),
441                 eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
442                 eq(testPkgName), eq(testAttributionTag), anyInt());
443         reset(mService);
444     }
445 
makeMessage(NetworkRequest req, int messageType)446     static Message makeMessage(NetworkRequest req, int messageType) {
447         Bundle bundle = new Bundle();
448         bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
449         // Pass default objects as we don't care which get passed here
450         bundle.putParcelable(Network.class.getSimpleName(), new Network(1));
451         bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities());
452         bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties());
453         Message msg = Message.obtain();
454         msg.what = messageType;
455         msg.setData(bundle);
456         return msg;
457     }
458 
makeRequest(int requestId)459     static NetworkRequest makeRequest(int requestId) {
460         NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
461         return new NetworkRequest(request.networkCapabilities, ConnectivityManager.TYPE_NONE,
462                 requestId, NetworkRequest.Type.NONE);
463     }
464 
expectThrowable(Runnable block, Class<? extends Throwable> throwableType)465     static void expectThrowable(Runnable block, Class<? extends Throwable> throwableType) {
466         try {
467             block.run();
468         } catch (Throwable t) {
469             if (t.getClass().equals(throwableType)) {
470                 return;
471             }
472             fail("expected exception of type " + throwableType + ", but was " + t.getClass());
473         }
474         fail("expected exception of type " + throwableType);
475     }
476 
477     private static class MockContext extends BroadcastInterceptingContext {
MockContext(Context base)478         MockContext(Context base) {
479             super(base);
480         }
481 
482         @Override
getApplicationContext()483         public Context getApplicationContext() {
484             return mock(Context.class);
485         }
486     }
487 
makeConnectivityManagerAndReturnContext()488     private WeakReference<Context> makeConnectivityManagerAndReturnContext() {
489         // Mockito may have an internal reference to the mock, creating MockContext for testing.
490         final Context c = new MockContext(mock(Context.class));
491 
492         new ConnectivityManager(c, mService);
493 
494         return new WeakReference<>(c);
495     }
496 
forceGC()497     private void forceGC() {
498         // First GC ensures that objects are collected for finalization, then second GC ensures
499         // they're garbage-collected after being finalized.
500         System.gc();
501         System.runFinalization();
502         System.gc();
503     }
504 
505     @Test
testConnectivityManagerDoesNotLeakContext()506     public void testConnectivityManagerDoesNotLeakContext() throws Exception {
507         final WeakReference<Context> ref = makeConnectivityManagerAndReturnContext();
508 
509         final int attempts = 600;
510         final long waitIntervalMs = 50;
511         for (int i = 0; i < attempts; i++) {
512             forceGC();
513             if (ref.get() == null) break;
514 
515             Thread.sleep(waitIntervalMs);
516         }
517 
518         assertNull("ConnectivityManager weak reference still not null after " + attempts
519                     + " attempts", ref.get());
520     }
521 
522     @Test
testDeclaredMethodsFlag_requestWithMixedMethods_RegistrationFlagsMatch()523     public void testDeclaredMethodsFlag_requestWithMixedMethods_RegistrationFlagsMatch()
524             throws Exception {
525         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
526                 .when(mService).getEnabledConnectivityManagerFeatures();
527         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
528 
529         final NetworkRequest request = new NetworkRequest.Builder().build();
530         final NetworkCallback callback1 = new ConnectivityManager.NetworkCallback() {
531             @Override
532             public void onPreCheck(@NonNull Network network) {}
533             @Override
534             public void onAvailable(@NonNull Network network) {}
535             @Override
536             public void onLost(@NonNull Network network) {}
537             @Override
538             public void onCapabilitiesChanged(@NonNull Network network,
539                     @NonNull NetworkCapabilities networkCapabilities) {}
540             @Override
541             public void onLocalNetworkInfoChanged(@NonNull Network network,
542                     @NonNull LocalNetworkInfo localNetworkInfo) {}
543             @Override
544             public void onNetworkResumed(@NonNull Network network) {}
545             @Override
546             public void onBlockedStatusChanged(@NonNull Network network, int blocked) {}
547         };
548         manager.requestNetwork(request, callback1);
549 
550         final InOrder inOrder = inOrder(mService);
551         inOrder.verify(mService).requestNetwork(
552                 anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), any(), any(),
553                 eq(1 << ConnectivityManager.CALLBACK_PRECHECK
554                         | 1 << ConnectivityManager.CALLBACK_AVAILABLE
555                         | 1 << ConnectivityManager.CALLBACK_LOST
556                         | 1 << ConnectivityManager.CALLBACK_CAP_CHANGED
557                         | 1 << ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED
558                         | 1 << ConnectivityManager.CALLBACK_RESUMED
559                         | 1 << ConnectivityManager.CALLBACK_BLK_CHANGED));
560     }
561 
562     @Test
testDeclaredMethodsFlag_listenWithMixedMethods_RegistrationFlagsMatch()563     public void testDeclaredMethodsFlag_listenWithMixedMethods_RegistrationFlagsMatch()
564             throws Exception {
565         final NetworkRequest request = new NetworkRequest.Builder().build();
566         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
567                 .when(mService).getEnabledConnectivityManagerFeatures();
568         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
569 
570         final NetworkCallback callback2 = new ConnectivityManager.NetworkCallback() {
571             @Override
572             public void onLosing(@NonNull Network network, int maxMsToLive) {}
573             @Override
574             public void onUnavailable() {}
575             @Override
576             public void onLinkPropertiesChanged(@NonNull Network network,
577                     @NonNull LinkProperties linkProperties) {}
578             @Override
579             public void onNetworkSuspended(@NonNull Network network) {}
580         };
581         manager.registerNetworkCallback(request, callback2);
582         // Call a second time with the same callback to exercise caching
583         manager.registerNetworkCallback(request, callback2);
584 
585         verify(mService, times(2)).listenForNetwork(
586                 any(), any(), any(), anyInt(), any(), any(),
587                 eq(1 << ConnectivityManager.CALLBACK_LOSING
588                         // AVAILABLE calls IP_CHANGED and SUSPENDED so it gets added
589                         | 1 << ConnectivityManager.CALLBACK_AVAILABLE
590                         | 1 << ConnectivityManager.CALLBACK_UNAVAIL
591                         | 1 << ConnectivityManager.CALLBACK_IP_CHANGED
592                         | 1 << ConnectivityManager.CALLBACK_SUSPENDED));
593     }
594 
595     @Test
testDeclaredMethodsFlag_requestWithHiddenAvailableCallback_RegistrationFlagsMatch()596     public void testDeclaredMethodsFlag_requestWithHiddenAvailableCallback_RegistrationFlagsMatch()
597             throws Exception {
598         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
599                 .when(mService).getEnabledConnectivityManagerFeatures();
600         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
601 
602         final NetworkCallback hiddenOnAvailableCb = new ConnectivityManager.NetworkCallback() {
603             // This overload is @hide but might still be used by (bad) apps
604             @Override
605             public void onAvailable(@NonNull Network network,
606                     @NonNull NetworkCapabilities networkCapabilities,
607                     @NonNull LinkProperties linkProperties, boolean blocked) {}
608         };
609         manager.registerDefaultNetworkCallback(hiddenOnAvailableCb);
610 
611         verify(mService).requestNetwork(
612                 anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), any(), any(),
613                 eq(1 << ConnectivityManager.CALLBACK_AVAILABLE));
614     }
615 
616     public static class NetworkCallbackWithOnLostOnly extends NetworkCallback {
617         @Override
onLost(@onNull Network network)618         public void onLost(@NonNull Network network) {}
619     }
620 
621     @Test
testDeclaredMethodsFlag_requestWithoutAvailableCallback_RegistrationFlagsMatch()622     public void testDeclaredMethodsFlag_requestWithoutAvailableCallback_RegistrationFlagsMatch()
623             throws Exception {
624         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
625                 .when(mService).getEnabledConnectivityManagerFeatures();
626         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
627         final Handler handler = new Handler(Looper.getMainLooper());
628 
629         final NetworkCallback noOnAvailableCb = new NetworkCallbackWithOnLostOnly();
630         manager.registerSystemDefaultNetworkCallback(noOnAvailableCb, handler);
631 
632         verify(mService).requestNetwork(
633                 anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), any(), any(),
634                 eq(1 << ConnectivityManager.CALLBACK_LOST));
635     }
636 
637     @Test
testDeclaredMethodsFlag_listenWithMock_OptimizationDisabled()638     public void testDeclaredMethodsFlag_listenWithMock_OptimizationDisabled()
639             throws Exception {
640         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
641                 .when(mService).getEnabledConnectivityManagerFeatures();
642         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
643         final Handler handler = new Handler(Looper.getMainLooper());
644 
645         final NetworkRequest request = new NetworkRequest.Builder().build();
646         manager.registerNetworkCallback(request, mock(NetworkCallbackWithOnLostOnly.class),
647                 handler);
648 
649         verify(mService).listenForNetwork(
650                 any(), any(), any(), anyInt(), any(), any(),
651                 // Mock that does not call the constructor -> do not use the optimization
652                 eq(~0));
653     }
654 
655     @Test
testDeclaredMethodsFlag_requestWitNoCallback_OptimizationDisabled()656     public void testDeclaredMethodsFlag_requestWitNoCallback_OptimizationDisabled()
657             throws Exception {
658         doReturn(ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS)
659                 .when(mService).getEnabledConnectivityManagerFeatures();
660         final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
661         final Handler handler = new Handler(Looper.getMainLooper());
662 
663         final NetworkRequest request = new NetworkRequest.Builder().build();
664         final NetworkCallback noCallbackAtAll = new ConnectivityManager.NetworkCallback() {};
665         manager.requestBackgroundNetwork(request, noCallbackAtAll, handler);
666 
667         verify(mService).requestNetwork(
668                 anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), any(), any(),
669                 // No callbacks overridden -> do not use the optimization
670                 eq(~0));
671     }
672 }
673