• 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 com.android.server.connectivity.tethering;
18 
19 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
20 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
21 import static android.net.ConnectivityManager.TYPE_NONE;
22 import static android.net.ConnectivityManager.TYPE_WIFI;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
24 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
25 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
26 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
27 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertFalse;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assert.fail;
32 import static org.mockito.Mockito.any;
33 import static org.mockito.Mockito.anyInt;
34 import static org.mockito.Mockito.anyString;
35 import static org.mockito.Mockito.eq;
36 import static org.mockito.Mockito.reset;
37 import static org.mockito.Mockito.spy;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.verifyNoMoreInteractions;
41 import static org.mockito.Mockito.when;
42 
43 import android.content.Context;
44 import android.net.ConnectivityManager;
45 import android.net.ConnectivityManager.NetworkCallback;
46 import android.net.IConnectivityManager;
47 import android.net.IpPrefix;
48 import android.net.LinkAddress;
49 import android.net.LinkProperties;
50 import android.net.Network;
51 import android.net.NetworkCapabilities;
52 import android.net.NetworkRequest;
53 import android.net.NetworkState;
54 import android.net.util.SharedLog;
55 import android.os.Handler;
56 import android.os.Message;
57 
58 import androidx.test.filters.SmallTest;
59 import androidx.test.runner.AndroidJUnit4;
60 
61 import com.android.internal.util.State;
62 import com.android.internal.util.StateMachine;
63 
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.Mock;
69 import org.mockito.MockitoAnnotations;
70 
71 import java.util.ArrayList;
72 import java.util.Collection;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Map;
77 import java.util.Objects;
78 import java.util.Set;
79 
80 @RunWith(AndroidJUnit4.class)
81 @SmallTest
82 public class UpstreamNetworkMonitorTest {
83     private static final int EVENT_UNM_UPDATE = 1;
84 
85     private static final boolean INCLUDES = true;
86     private static final boolean EXCLUDES = false;
87 
88     // Actual contents of the request don't matter for this test. The lack of
89     // any specific TRANSPORT_* is sufficient to identify this request.
90     private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
91 
92     @Mock private Context mContext;
93     @Mock private EntitlementManager mEntitleMgr;
94     @Mock private IConnectivityManager mCS;
95     @Mock private SharedLog mLog;
96 
97     private TestStateMachine mSM;
98     private TestConnectivityManager mCM;
99     private UpstreamNetworkMonitor mUNM;
100 
setUp()101     @Before public void setUp() throws Exception {
102         MockitoAnnotations.initMocks(this);
103         reset(mContext);
104         reset(mCS);
105         reset(mLog);
106         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
107         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
108 
109         mCM = spy(new TestConnectivityManager(mContext, mCS));
110         mSM = new TestStateMachine();
111         mUNM = new UpstreamNetworkMonitor(
112                 (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
113     }
114 
tearDown()115     @After public void tearDown() throws Exception {
116         if (mSM != null) {
117             mSM.quit();
118             mSM = null;
119         }
120     }
121 
122     @Test
testStopWithoutStartIsNonFatal()123     public void testStopWithoutStartIsNonFatal() {
124         mUNM.stop();
125         mUNM.stop();
126         mUNM.stop();
127     }
128 
129     @Test
testDoesNothingBeforeTrackDefaultAndStarted()130     public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception {
131         assertTrue(mCM.hasNoCallbacks());
132         assertFalse(mUNM.mobileNetworkRequested());
133 
134         mUNM.updateMobileRequiresDun(true);
135         assertTrue(mCM.hasNoCallbacks());
136         mUNM.updateMobileRequiresDun(false);
137         assertTrue(mCM.hasNoCallbacks());
138     }
139 
140     @Test
testDefaultNetworkIsTracked()141     public void testDefaultNetworkIsTracked() throws Exception {
142         assertTrue(mCM.hasNoCallbacks());
143         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
144 
145         mUNM.startObserveAllNetworks();
146         assertEquals(1, mCM.trackingDefault.size());
147 
148         mUNM.stop();
149         assertTrue(mCM.onlyHasDefaultCallbacks());
150     }
151 
152     @Test
testListensForAllNetworks()153     public void testListensForAllNetworks() throws Exception {
154         assertTrue(mCM.listening.isEmpty());
155 
156         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
157         mUNM.startObserveAllNetworks();
158         assertFalse(mCM.listening.isEmpty());
159         assertTrue(mCM.isListeningForAll());
160 
161         mUNM.stop();
162         assertTrue(mCM.onlyHasDefaultCallbacks());
163     }
164 
165     @Test
testCallbacksRegistered()166     public void testCallbacksRegistered() {
167         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
168         verify(mCM, times(1)).requestNetwork(
169                 eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
170         mUNM.startObserveAllNetworks();
171         verify(mCM, times(1)).registerNetworkCallback(
172                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
173 
174         mUNM.stop();
175         verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
176     }
177 
178     @Test
testRequestsMobileNetwork()179     public void testRequestsMobileNetwork() throws Exception {
180         assertFalse(mUNM.mobileNetworkRequested());
181         assertEquals(0, mCM.requested.size());
182 
183         mUNM.startObserveAllNetworks();
184         assertFalse(mUNM.mobileNetworkRequested());
185         assertEquals(0, mCM.requested.size());
186 
187         mUNM.updateMobileRequiresDun(false);
188         assertFalse(mUNM.mobileNetworkRequested());
189         assertEquals(0, mCM.requested.size());
190 
191         mUNM.registerMobileNetworkRequest();
192         assertTrue(mUNM.mobileNetworkRequested());
193         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
194         assertFalse(mCM.isDunRequested());
195 
196         mUNM.stop();
197         assertFalse(mUNM.mobileNetworkRequested());
198         assertTrue(mCM.hasNoCallbacks());
199     }
200 
201     @Test
testDuplicateMobileRequestsIgnored()202     public void testDuplicateMobileRequestsIgnored() throws Exception {
203         assertFalse(mUNM.mobileNetworkRequested());
204         assertEquals(0, mCM.requested.size());
205 
206         mUNM.startObserveAllNetworks();
207         verify(mCM, times(1)).registerNetworkCallback(
208                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
209         assertFalse(mUNM.mobileNetworkRequested());
210         assertEquals(0, mCM.requested.size());
211 
212         mUNM.updateMobileRequiresDun(true);
213         mUNM.registerMobileNetworkRequest();
214         verify(mCM, times(1)).requestNetwork(
215                 any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
216                 any(Handler.class));
217 
218         assertTrue(mUNM.mobileNetworkRequested());
219         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
220         assertTrue(mCM.isDunRequested());
221 
222         // Try a few things that must not result in any state change.
223         mUNM.registerMobileNetworkRequest();
224         mUNM.updateMobileRequiresDun(true);
225         mUNM.registerMobileNetworkRequest();
226 
227         assertTrue(mUNM.mobileNetworkRequested());
228         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
229         assertTrue(mCM.isDunRequested());
230 
231         mUNM.stop();
232         verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
233 
234         verifyNoMoreInteractions(mCM);
235     }
236 
237     @Test
testRequestsDunNetwork()238     public void testRequestsDunNetwork() throws Exception {
239         assertFalse(mUNM.mobileNetworkRequested());
240         assertEquals(0, mCM.requested.size());
241 
242         mUNM.startObserveAllNetworks();
243         assertFalse(mUNM.mobileNetworkRequested());
244         assertEquals(0, mCM.requested.size());
245 
246         mUNM.updateMobileRequiresDun(true);
247         assertFalse(mUNM.mobileNetworkRequested());
248         assertEquals(0, mCM.requested.size());
249 
250         mUNM.registerMobileNetworkRequest();
251         assertTrue(mUNM.mobileNetworkRequested());
252         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
253         assertTrue(mCM.isDunRequested());
254 
255         mUNM.stop();
256         assertFalse(mUNM.mobileNetworkRequested());
257         assertTrue(mCM.hasNoCallbacks());
258     }
259 
260     @Test
testUpdateMobileRequiresDun()261     public void testUpdateMobileRequiresDun() throws Exception {
262         mUNM.startObserveAllNetworks();
263 
264         // Test going from no-DUN to DUN correctly re-registers callbacks.
265         mUNM.updateMobileRequiresDun(false);
266         mUNM.registerMobileNetworkRequest();
267         assertTrue(mUNM.mobileNetworkRequested());
268         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
269         assertFalse(mCM.isDunRequested());
270         mUNM.updateMobileRequiresDun(true);
271         assertTrue(mUNM.mobileNetworkRequested());
272         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
273         assertTrue(mCM.isDunRequested());
274 
275         // Test going from DUN to no-DUN correctly re-registers callbacks.
276         mUNM.updateMobileRequiresDun(false);
277         assertTrue(mUNM.mobileNetworkRequested());
278         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
279         assertFalse(mCM.isDunRequested());
280 
281         mUNM.stop();
282         assertFalse(mUNM.mobileNetworkRequested());
283     }
284 
285     @Test
testSelectPreferredUpstreamType()286     public void testSelectPreferredUpstreamType() throws Exception {
287         final Collection<Integer> preferredTypes = new ArrayList<>();
288         preferredTypes.add(TYPE_WIFI);
289 
290         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
291         mUNM.startObserveAllNetworks();
292         // There are no networks, so there is nothing to select.
293         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
294 
295         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
296         wifiAgent.fakeConnect();
297         // WiFi is up, we should prefer it.
298         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
299         wifiAgent.fakeDisconnect();
300         // There are no networks, so there is nothing to select.
301         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
302 
303         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
304         cellAgent.fakeConnect();
305         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
306 
307         preferredTypes.add(TYPE_MOBILE_DUN);
308         // This is coupled with preferred types in TetheringConfiguration.
309         mUNM.updateMobileRequiresDun(true);
310         // DUN is available, but only use regular cell: no upstream selected.
311         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
312         preferredTypes.remove(TYPE_MOBILE_DUN);
313         // No WiFi, but our preferred flavour of cell is up.
314         preferredTypes.add(TYPE_MOBILE_HIPRI);
315         // This is coupled with preferred types in TetheringConfiguration.
316         mUNM.updateMobileRequiresDun(false);
317         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
318                 mUNM.selectPreferredUpstreamType(preferredTypes));
319         // Check to see we filed an explicit request.
320         assertEquals(1, mCM.requested.size());
321         NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
322         assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
323         assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
324         // mobile is not permitted, we should not use HIPRI.
325         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
326         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
327         assertEquals(0, mCM.requested.size());
328         // mobile change back to permitted, HIRPI should come back
329         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
330         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
331                 mUNM.selectPreferredUpstreamType(preferredTypes));
332 
333         wifiAgent.fakeConnect();
334         // WiFi is up, and we should prefer it over cell.
335         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
336         assertEquals(0, mCM.requested.size());
337 
338         preferredTypes.remove(TYPE_MOBILE_HIPRI);
339         preferredTypes.add(TYPE_MOBILE_DUN);
340         // This is coupled with preferred types in TetheringConfiguration.
341         mUNM.updateMobileRequiresDun(true);
342         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
343 
344         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
345         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
346         dunAgent.fakeConnect();
347 
348         // WiFi is still preferred.
349         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
350 
351         // WiFi goes down, cell and DUN are still up but only DUN is preferred.
352         wifiAgent.fakeDisconnect();
353         assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
354                 mUNM.selectPreferredUpstreamType(preferredTypes));
355         // Check to see we filed an explicit request.
356         assertEquals(1, mCM.requested.size());
357         netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
358         assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
359         assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
360         // mobile is not permitted, we should not use DUN.
361         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
362         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
363         assertEquals(0, mCM.requested.size());
364         // mobile change back to permitted, DUN should come back
365         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
366         assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
367                 mUNM.selectPreferredUpstreamType(preferredTypes));
368     }
369 
370     @Test
testGetCurrentPreferredUpstream()371     public void testGetCurrentPreferredUpstream() throws Exception {
372         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
373         mUNM.startObserveAllNetworks();
374         mUNM.updateMobileRequiresDun(false);
375 
376         // [0] Mobile connects, DUN not required -> mobile selected.
377         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
378         cellAgent.fakeConnect();
379         mCM.makeDefaultNetwork(cellAgent);
380         assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
381 
382         // [1] Mobile connects but not permitted -> null selected
383         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
384         assertEquals(null, mUNM.getCurrentPreferredUpstream());
385         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
386 
387         // [2] WiFi connects but not validated/promoted to default -> mobile selected.
388         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
389         wifiAgent.fakeConnect();
390         assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
391 
392         // [3] WiFi validates and is promoted to the default network -> WiFi selected.
393         mCM.makeDefaultNetwork(wifiAgent);
394         assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
395 
396         // [4] DUN required, no other changes -> WiFi still selected
397         mUNM.updateMobileRequiresDun(true);
398         assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
399 
400         // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
401         mCM.makeDefaultNetwork(cellAgent);
402         assertEquals(null, mUNM.getCurrentPreferredUpstream());
403         // TODO: make sure that a DUN request has been filed. This is currently
404         // triggered by code over in Tethering, but once that has been moved
405         // into UNM we should test for this here.
406 
407         // [6] DUN network arrives -> DUN selected
408         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
409         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
410         dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
411         dunAgent.fakeConnect();
412         assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
413 
414         // [7] Mobile is not permitted -> null selected
415         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
416         assertEquals(null, mUNM.getCurrentPreferredUpstream());
417     }
418 
419     @Test
testLocalPrefixes()420     public void testLocalPrefixes() throws Exception {
421         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
422         mUNM.startObserveAllNetworks();
423 
424         // [0] Test minimum set of local prefixes.
425         Set<IpPrefix> local = mUNM.getLocalPrefixes();
426         assertTrue(local.isEmpty());
427 
428         final Set<String> alreadySeen = new HashSet<>();
429 
430         // [1] Pretend Wi-Fi connects.
431         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
432         final LinkProperties wifiLp = wifiAgent.linkProperties;
433         wifiLp.setInterfaceName("wlan0");
434         final String[] WIFI_ADDRS = {
435                 "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
436                 "2001:db8:4:fd00:827a:bfff:fe6f:374d",
437                 "2001:db8:4:fd00:6dea:325a:fdae:4ef4",
438                 "fd6a:a640:60bf:e985::123",  // ULA address for good measure.
439         };
440         for (String addrStr : WIFI_ADDRS) {
441             final String cidr = addrStr.contains(":") ? "/64" : "/20";
442             wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
443         }
444         wifiAgent.fakeConnect();
445         wifiAgent.sendLinkProperties();
446 
447         local = mUNM.getLocalPrefixes();
448         assertPrefixSet(local, INCLUDES, alreadySeen);
449         final String[] wifiLinkPrefixes = {
450                 // Link-local prefixes are excluded and dealt with elsewhere.
451                 "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
452         };
453         assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
454         Collections.addAll(alreadySeen, wifiLinkPrefixes);
455         assertEquals(alreadySeen.size(), local.size());
456 
457         // [2] Pretend mobile connects.
458         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
459         final LinkProperties cellLp = cellAgent.linkProperties;
460         cellLp.setInterfaceName("rmnet_data0");
461         final String[] CELL_ADDRS = {
462                 "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
463         };
464         for (String addrStr : CELL_ADDRS) {
465             final String cidr = addrStr.contains(":") ? "/64" : "/27";
466             cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
467         }
468         cellAgent.fakeConnect();
469         cellAgent.sendLinkProperties();
470 
471         local = mUNM.getLocalPrefixes();
472         assertPrefixSet(local, INCLUDES, alreadySeen);
473         final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
474         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
475         Collections.addAll(alreadySeen, cellLinkPrefixes);
476         assertEquals(alreadySeen.size(), local.size());
477 
478         // [3] Pretend DUN connects.
479         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
480         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
481         dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
482         final LinkProperties dunLp = dunAgent.linkProperties;
483         dunLp.setInterfaceName("rmnet_data1");
484         final String[] DUN_ADDRS = {
485                 "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
486         };
487         for (String addrStr : DUN_ADDRS) {
488             final String cidr = addrStr.contains(":") ? "/64" : "/27";
489             dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
490         }
491         dunAgent.fakeConnect();
492         dunAgent.sendLinkProperties();
493 
494         local = mUNM.getLocalPrefixes();
495         assertPrefixSet(local, INCLUDES, alreadySeen);
496         final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
497         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
498         Collections.addAll(alreadySeen, dunLinkPrefixes);
499         assertEquals(alreadySeen.size(), local.size());
500 
501         // [4] Pretend Wi-Fi disconnected.  It's addresses/prefixes should no
502         // longer be included (should be properly removed).
503         wifiAgent.fakeDisconnect();
504         local = mUNM.getLocalPrefixes();
505         assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
506         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
507         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
508 
509         // [5] Pretend mobile disconnected.
510         cellAgent.fakeDisconnect();
511         local = mUNM.getLocalPrefixes();
512         assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
513         assertPrefixSet(local, EXCLUDES, cellLinkPrefixes);
514         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
515 
516         // [6] Pretend DUN disconnected.
517         dunAgent.fakeDisconnect();
518         local = mUNM.getLocalPrefixes();
519         assertTrue(local.isEmpty());
520     }
521 
522     @Test
testSelectMobileWhenMobileIsNotDefault()523     public void testSelectMobileWhenMobileIsNotDefault() {
524         final Collection<Integer> preferredTypes = new ArrayList<>();
525         // Mobile has higher pirority than wifi.
526         preferredTypes.add(TYPE_MOBILE_HIPRI);
527         preferredTypes.add(TYPE_WIFI);
528         mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
529         mUNM.startObserveAllNetworks();
530         // Setup wifi and make wifi as default network.
531         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
532         wifiAgent.fakeConnect();
533         mCM.makeDefaultNetwork(wifiAgent);
534         // Setup mobile network.
535         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
536         cellAgent.fakeConnect();
537 
538         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
539                 mUNM.selectPreferredUpstreamType(preferredTypes));
540         verify(mEntitleMgr, times(1)).maybeRunProvisioning();
541     }
assertSatisfiesLegacyType(int legacyType, NetworkState ns)542     private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
543         if (legacyType == TYPE_NONE) {
544             assertTrue(ns == null);
545             return;
546         }
547 
548         final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
549         assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
550     }
551 
assertUpstreamTypeRequested(int upstreamType)552     private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
553         assertEquals(1, mCM.requested.size());
554         assertEquals(1, mCM.legacyTypeMap.size());
555         assertEquals(Integer.valueOf(upstreamType),
556                 mCM.legacyTypeMap.values().iterator().next());
557     }
558 
559     public static class TestConnectivityManager extends ConnectivityManager {
560         public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
561         public Set<NetworkCallback> trackingDefault = new HashSet<>();
562         public TestNetworkAgent defaultNetwork = null;
563         public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
564         public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
565         public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
566 
567         private int mNetworkId = 100;
568 
TestConnectivityManager(Context ctx, IConnectivityManager svc)569         public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
570             super(ctx, svc);
571         }
572 
hasNoCallbacks()573         boolean hasNoCallbacks() {
574             return allCallbacks.isEmpty()
575                     && trackingDefault.isEmpty()
576                     && listening.isEmpty()
577                     && requested.isEmpty()
578                     && legacyTypeMap.isEmpty();
579         }
580 
onlyHasDefaultCallbacks()581         boolean onlyHasDefaultCallbacks() {
582             return (allCallbacks.size() == 1)
583                     && (trackingDefault.size() == 1)
584                     && listening.isEmpty()
585                     && requested.isEmpty()
586                     && legacyTypeMap.isEmpty();
587         }
588 
isListeningForAll()589         boolean isListeningForAll() {
590             final NetworkCapabilities empty = new NetworkCapabilities();
591             empty.clearAll();
592 
593             for (NetworkRequest req : listening.values()) {
594                 if (req.networkCapabilities.equalRequestableCapabilities(empty)) {
595                     return true;
596                 }
597             }
598             return false;
599         }
600 
isDunRequested()601         boolean isDunRequested() {
602             for (NetworkRequest req : requested.values()) {
603                 if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
604                     return true;
605                 }
606             }
607             return false;
608         }
609 
getNetworkId()610         int getNetworkId() { return ++mNetworkId; }
611 
makeDefaultNetwork(TestNetworkAgent agent)612         void makeDefaultNetwork(TestNetworkAgent agent) {
613             if (Objects.equals(defaultNetwork, agent)) return;
614 
615             final TestNetworkAgent formerDefault = defaultNetwork;
616             defaultNetwork = agent;
617 
618             for (NetworkCallback cb : trackingDefault) {
619                 if (defaultNetwork != null) {
620                     cb.onAvailable(defaultNetwork.networkId);
621                     cb.onCapabilitiesChanged(
622                             defaultNetwork.networkId, defaultNetwork.networkCapabilities);
623                     cb.onLinkPropertiesChanged(
624                             defaultNetwork.networkId, defaultNetwork.linkProperties);
625                 }
626             }
627         }
628 
629         @Override
requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h)630         public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
631             assertFalse(allCallbacks.containsKey(cb));
632             allCallbacks.put(cb, h);
633             if (mDefaultRequest.equals(req)) {
634                 assertFalse(trackingDefault.contains(cb));
635                 trackingDefault.add(cb);
636             } else {
637                 assertFalse(requested.containsKey(cb));
638                 requested.put(cb, req);
639             }
640         }
641 
642         @Override
requestNetwork(NetworkRequest req, NetworkCallback cb)643         public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
644             fail("Should never be called.");
645         }
646 
647         @Override
requestNetwork(NetworkRequest req, NetworkCallback cb, int timeoutMs, int legacyType, Handler h)648         public void requestNetwork(NetworkRequest req, NetworkCallback cb,
649                 int timeoutMs, int legacyType, Handler h) {
650             assertFalse(allCallbacks.containsKey(cb));
651             allCallbacks.put(cb, h);
652             assertFalse(requested.containsKey(cb));
653             requested.put(cb, req);
654             assertFalse(legacyTypeMap.containsKey(cb));
655             if (legacyType != ConnectivityManager.TYPE_NONE) {
656                 legacyTypeMap.put(cb, legacyType);
657             }
658         }
659 
660         @Override
registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h)661         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
662             assertFalse(allCallbacks.containsKey(cb));
663             allCallbacks.put(cb, h);
664             assertFalse(listening.containsKey(cb));
665             listening.put(cb, req);
666         }
667 
668         @Override
registerNetworkCallback(NetworkRequest req, NetworkCallback cb)669         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
670             fail("Should never be called.");
671         }
672 
673         @Override
registerDefaultNetworkCallback(NetworkCallback cb, Handler h)674         public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
675             fail("Should never be called.");
676         }
677 
678         @Override
registerDefaultNetworkCallback(NetworkCallback cb)679         public void registerDefaultNetworkCallback(NetworkCallback cb) {
680             fail("Should never be called.");
681         }
682 
683         @Override
unregisterNetworkCallback(NetworkCallback cb)684         public void unregisterNetworkCallback(NetworkCallback cb) {
685             if (trackingDefault.contains(cb)) {
686                 trackingDefault.remove(cb);
687             } else if (listening.containsKey(cb)) {
688                 listening.remove(cb);
689             } else if (requested.containsKey(cb)) {
690                 requested.remove(cb);
691                 legacyTypeMap.remove(cb);
692             } else {
693                 fail("Unexpected callback removed");
694             }
695             allCallbacks.remove(cb);
696 
697             assertFalse(allCallbacks.containsKey(cb));
698             assertFalse(trackingDefault.contains(cb));
699             assertFalse(listening.containsKey(cb));
700             assertFalse(requested.containsKey(cb));
701         }
702     }
703 
704     public static class TestNetworkAgent {
705         public final TestConnectivityManager cm;
706         public final Network networkId;
707         public final int transportType;
708         public final NetworkCapabilities networkCapabilities;
709         public final LinkProperties linkProperties;
710 
TestNetworkAgent(TestConnectivityManager cm, int transportType)711         public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
712             this.cm = cm;
713             this.networkId = new Network(cm.getNetworkId());
714             this.transportType = transportType;
715             networkCapabilities = new NetworkCapabilities();
716             networkCapabilities.addTransportType(transportType);
717             networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
718             linkProperties = new LinkProperties();
719         }
720 
fakeConnect()721         public void fakeConnect() {
722             for (NetworkCallback cb : cm.listening.keySet()) {
723                 cb.onAvailable(networkId);
724                 cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
725                 cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
726             }
727         }
728 
fakeDisconnect()729         public void fakeDisconnect() {
730             for (NetworkCallback cb : cm.listening.keySet()) {
731                 cb.onLost(networkId);
732             }
733         }
734 
sendLinkProperties()735         public void sendLinkProperties() {
736             for (NetworkCallback cb : cm.listening.keySet()) {
737                 cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
738             }
739         }
740 
741         @Override
toString()742         public String toString() {
743             return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
744         }
745     }
746 
747     public static class TestStateMachine extends StateMachine {
748         public final ArrayList<Message> messages = new ArrayList<>();
749         private final State mLoggingState = new LoggingState();
750 
751         class LoggingState extends State {
enter()752             @Override public void enter() { messages.clear(); }
753 
exit()754             @Override public void exit() { messages.clear(); }
755 
processMessage(Message msg)756             @Override public boolean processMessage(Message msg) {
757                 messages.add(msg);
758                 return true;
759             }
760         }
761 
TestStateMachine()762         public TestStateMachine() {
763             super("UpstreamNetworkMonitor.TestStateMachine");
764             addState(mLoggingState);
765             setInitialState(mLoggingState);
766             super.start();
767         }
768     }
769 
copy(NetworkCapabilities nc)770     static NetworkCapabilities copy(NetworkCapabilities nc) {
771         return new NetworkCapabilities(nc);
772     }
773 
copy(LinkProperties lp)774     static LinkProperties copy(LinkProperties lp) {
775         return new LinkProperties(lp);
776     }
777 
assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected)778     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
779         final Set<String> expectedSet = new HashSet<>();
780         Collections.addAll(expectedSet, expected);
781         assertPrefixSet(prefixes, expectation, expectedSet);
782     }
783 
assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected)784     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
785         for (String expectedPrefix : expected) {
786             final String errStr = expectation ? "did not find" : "found";
787             assertEquals(
788                     String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
789                     expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
790         }
791     }
792 }
793