• 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.networkstack.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_WIFI;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
23 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
25 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
26 
27 import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
28 import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.ArgumentMatchers.eq;
34 import static org.mockito.Mockito.any;
35 import static org.mockito.Mockito.anyInt;
36 import static org.mockito.Mockito.anyString;
37 import static org.mockito.Mockito.reset;
38 import static org.mockito.Mockito.spy;
39 import static org.mockito.Mockito.times;
40 import static org.mockito.Mockito.verify;
41 import static org.mockito.Mockito.verifyNoMoreInteractions;
42 import static org.mockito.Mockito.when;
43 
44 import android.content.Context;
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.NetworkCapabilities;
51 import android.net.NetworkRequest;
52 import android.net.util.SharedLog;
53 import android.os.Handler;
54 import android.os.Looper;
55 import android.os.Message;
56 import android.os.test.TestLooper;
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 import com.android.networkstack.tethering.TestConnectivityManager.NetworkRequestInfo;
64 import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
65 
66 import org.junit.After;
67 import org.junit.Before;
68 import org.junit.Test;
69 import org.junit.runner.RunWith;
70 import org.mockito.ArgumentCaptor;
71 import org.mockito.Mock;
72 import org.mockito.MockitoAnnotations;
73 
74 import java.util.ArrayList;
75 import java.util.Collection;
76 import java.util.Collections;
77 import java.util.HashSet;
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     private static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities.Builder()
89             .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_INTERNET).build();
90     private static final NetworkCapabilities DUN_CAPABILITIES = new NetworkCapabilities.Builder()
91             .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_DUN).build();
92     private static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities.Builder()
93             .addTransportType(TRANSPORT_WIFI).addCapability(NET_CAPABILITY_INTERNET).build();
94 
95     @Mock private Context mContext;
96     @Mock private EntitlementManager mEntitleMgr;
97     @Mock private IConnectivityManager mCS;
98     @Mock private SharedLog mLog;
99 
100     private TestStateMachine mSM;
101     private TestConnectivityManager mCM;
102     private UpstreamNetworkMonitor mUNM;
103 
104     private final TestLooper mLooper = new TestLooper();
105 
setUp()106     @Before public void setUp() throws Exception {
107         MockitoAnnotations.initMocks(this);
108         reset(mContext);
109         reset(mCS);
110         reset(mLog);
111         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
112         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
113 
114         mCM = spy(new TestConnectivityManager(mContext, mCS));
115         when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(mCM);
116         mSM = new TestStateMachine(mLooper.getLooper());
117         mUNM = new UpstreamNetworkMonitor(mContext, mSM, mLog, EVENT_UNM_UPDATE);
118     }
119 
tearDown()120     @After public void tearDown() throws Exception {
121         if (mSM != null) {
122             mSM.quit();
123             mSM = null;
124         }
125     }
126 
127     @Test
testStopWithoutStartIsNonFatal()128     public void testStopWithoutStartIsNonFatal() {
129         mUNM.stop();
130         mUNM.stop();
131         mUNM.stop();
132     }
133 
134     @Test
testDoesNothingBeforeTrackDefaultAndStarted()135     public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception {
136         assertTrue(mCM.hasNoCallbacks());
137         assertFalse(mUNM.mobileNetworkRequested());
138 
139         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
140         assertTrue(mCM.hasNoCallbacks());
141         mUNM.setUpstreamConfig(false /* autoUpstream */, false /* dunRequired */);
142         assertTrue(mCM.hasNoCallbacks());
143     }
144 
145     @Test
testDefaultNetworkIsTracked()146     public void testDefaultNetworkIsTracked() throws Exception {
147         assertTrue(mCM.hasNoCallbacks());
148         mUNM.startTrackDefaultNetwork(mEntitleMgr);
149 
150         mUNM.startObserveAllNetworks();
151         assertEquals(1, mCM.mTrackingDefault.size());
152 
153         mUNM.stop();
154         assertTrue(mCM.onlyHasDefaultCallbacks());
155     }
156 
157     @Test
testListensForAllNetworks()158     public void testListensForAllNetworks() throws Exception {
159         assertTrue(mCM.mListening.isEmpty());
160 
161         mUNM.startTrackDefaultNetwork(mEntitleMgr);
162         mUNM.startObserveAllNetworks();
163         assertFalse(mCM.mListening.isEmpty());
164         assertTrue(mCM.isListeningForAll());
165 
166         mUNM.stop();
167         assertTrue(mCM.onlyHasDefaultCallbacks());
168     }
169 
170     @Test
testCallbacksRegistered()171     public void testCallbacksRegistered() {
172         mUNM.startTrackDefaultNetwork(mEntitleMgr);
173         // Verify the fired default request matches expectation.
174         final ArgumentCaptor<NetworkRequest> requestCaptor =
175                 ArgumentCaptor.forClass(NetworkRequest.class);
176 
177         if (isAtLeastS()) {
178             verify(mCM).registerSystemDefaultNetworkCallback(any(), any());
179         } else {
180             verify(mCM).requestNetwork(
181                     requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
182             // For R- devices, Tethering will invoke this function in 2 cases, one is to
183             // request mobile network, the other is to track system default network. Verify
184             // the request is the one tracks default network.
185             assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
186         }
187 
188         mUNM.startObserveAllNetworks();
189         verify(mCM, times(1)).registerNetworkCallback(
190                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
191 
192         mUNM.stop();
193         verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
194     }
195 
196     @Test
testRequestsMobileNetwork()197     public void testRequestsMobileNetwork() throws Exception {
198         assertFalse(mUNM.mobileNetworkRequested());
199         assertEquals(0, mCM.mRequested.size());
200 
201         mUNM.startObserveAllNetworks();
202         assertFalse(mUNM.mobileNetworkRequested());
203         assertEquals(0, mCM.mRequested.size());
204 
205         mUNM.setUpstreamConfig(false /* autoUpstream */, false /* dunRequired */);
206         assertFalse(mUNM.mobileNetworkRequested());
207         assertEquals(0, mCM.mRequested.size());
208 
209         mUNM.setTryCell(true);
210         assertTrue(mUNM.mobileNetworkRequested());
211         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
212         assertFalse(isDunRequested());
213 
214         mUNM.stop();
215         assertFalse(mUNM.mobileNetworkRequested());
216         assertTrue(mCM.hasNoCallbacks());
217     }
218 
219     @Test
testDuplicateMobileRequestsIgnored()220     public void testDuplicateMobileRequestsIgnored() throws Exception {
221         assertFalse(mUNM.mobileNetworkRequested());
222         assertEquals(0, mCM.mRequested.size());
223 
224         mUNM.startObserveAllNetworks();
225         verify(mCM, times(1)).registerNetworkCallback(
226                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
227         assertFalse(mUNM.mobileNetworkRequested());
228         assertEquals(0, mCM.mRequested.size());
229 
230         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
231         mUNM.setTryCell(true);
232         verify(mCM, times(1)).requestNetwork(
233                 any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class),
234                 any(NetworkCallback.class));
235 
236         assertTrue(mUNM.mobileNetworkRequested());
237         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
238         assertTrue(isDunRequested());
239 
240         // Try a few things that must not result in any state change.
241         mUNM.setTryCell(true);
242         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
243         mUNM.setTryCell(true);
244 
245         assertTrue(mUNM.mobileNetworkRequested());
246         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
247         assertTrue(isDunRequested());
248 
249         mUNM.stop();
250         verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
251 
252         verifyNoMoreInteractions(mCM);
253     }
254 
255     @Test
testRequestsDunNetwork()256     public void testRequestsDunNetwork() throws Exception {
257         assertFalse(mUNM.mobileNetworkRequested());
258         assertEquals(0, mCM.mRequested.size());
259 
260         mUNM.startObserveAllNetworks();
261         assertFalse(mUNM.mobileNetworkRequested());
262         assertEquals(0, mCM.mRequested.size());
263 
264         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
265         assertFalse(mUNM.mobileNetworkRequested());
266         assertEquals(0, mCM.mRequested.size());
267 
268         mUNM.setTryCell(true);
269         assertTrue(mUNM.mobileNetworkRequested());
270         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
271         assertTrue(isDunRequested());
272 
273         mUNM.stop();
274         assertFalse(mUNM.mobileNetworkRequested());
275         assertTrue(mCM.hasNoCallbacks());
276     }
277 
278     @Test
testUpdateMobileRequiresDun()279     public void testUpdateMobileRequiresDun() throws Exception {
280         mUNM.startObserveAllNetworks();
281 
282         // Test going from no-DUN to DUN correctly re-registers callbacks.
283         mUNM.setUpstreamConfig(false /* autoUpstream */, false /* dunRequired */);
284         mUNM.setTryCell(true);
285         assertTrue(mUNM.mobileNetworkRequested());
286         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
287         assertFalse(isDunRequested());
288         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
289         assertTrue(mUNM.mobileNetworkRequested());
290         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
291         assertTrue(isDunRequested());
292 
293         // Test going from DUN to no-DUN correctly re-registers callbacks.
294         mUNM.setUpstreamConfig(false /* autoUpstream */, false /* dunRequired */);
295         assertTrue(mUNM.mobileNetworkRequested());
296         assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
297         assertFalse(isDunRequested());
298 
299         mUNM.stop();
300         assertFalse(mUNM.mobileNetworkRequested());
301     }
302 
303     @Test
testSelectPreferredUpstreamType()304     public void testSelectPreferredUpstreamType() throws Exception {
305         final Collection<Integer> preferredTypes = new ArrayList<>();
306         preferredTypes.add(TYPE_WIFI);
307 
308         mUNM.startTrackDefaultNetwork(mEntitleMgr);
309         mUNM.startObserveAllNetworks();
310         // There are no networks, so there is nothing to select.
311         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
312 
313         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
314         wifiAgent.fakeConnect();
315         mLooper.dispatchAll();
316         // WiFi is up, we should prefer it.
317         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
318         wifiAgent.fakeDisconnect();
319         mLooper.dispatchAll();
320         // There are no networks, so there is nothing to select.
321         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
322 
323         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
324         cellAgent.fakeConnect();
325         mLooper.dispatchAll();
326         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
327 
328         preferredTypes.add(TYPE_MOBILE_DUN);
329         // This is coupled with preferred types in TetheringConfiguration.
330         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
331         // DUN is available, but only use regular cell: no upstream selected.
332         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
333         preferredTypes.remove(TYPE_MOBILE_DUN);
334         // No WiFi, but our preferred flavour of cell is up.
335         preferredTypes.add(TYPE_MOBILE_HIPRI);
336         // This is coupled with preferred types in TetheringConfiguration.
337         mUNM.setUpstreamConfig(false /* autoUpstream */, false /* dunRequired */);
338         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
339                 mUNM.selectPreferredUpstreamType(preferredTypes));
340         // mobile is not permitted, we should not use HIPRI.
341         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
342         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
343         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
344         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
345                 mUNM.selectPreferredUpstreamType(preferredTypes));
346 
347         wifiAgent.fakeConnect();
348         mLooper.dispatchAll();
349         // WiFi is up, and we should prefer it over cell.
350         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
351 
352         preferredTypes.remove(TYPE_MOBILE_HIPRI);
353         preferredTypes.add(TYPE_MOBILE_DUN);
354         // This is coupled with preferred types in TetheringConfiguration.
355         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
356         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
357 
358         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
359         dunAgent.fakeConnect();
360         mLooper.dispatchAll();
361 
362         // WiFi is still preferred.
363         assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
364 
365         // WiFi goes down, cell and DUN are still up but only DUN is preferred.
366         wifiAgent.fakeDisconnect();
367         mLooper.dispatchAll();
368         assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
369                 mUNM.selectPreferredUpstreamType(preferredTypes));
370         // mobile is not permitted, we should not use DUN.
371         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
372         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
373         // mobile change back to permitted, DUN should come back
374         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
375         assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
376                 mUNM.selectPreferredUpstreamType(preferredTypes));
377     }
378 
379     @Test
testGetCurrentPreferredUpstream()380     public void testGetCurrentPreferredUpstream() throws Exception {
381         mUNM.startTrackDefaultNetwork(mEntitleMgr);
382         mUNM.startObserveAllNetworks();
383         mUNM.setUpstreamConfig(true /* autoUpstream */, false /* dunRequired */);
384         mUNM.setTryCell(true);
385 
386         // [0] Mobile connects, DUN not required -> mobile selected.
387         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
388         cellAgent.fakeConnect();
389         mCM.makeDefaultNetwork(cellAgent);
390         mLooper.dispatchAll();
391         assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
392         assertEquals(0, mCM.mRequested.size());
393 
394         // [1] Mobile connects but not permitted -> null selected
395         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
396         assertEquals(null, mUNM.getCurrentPreferredUpstream());
397         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
398         assertEquals(0, mCM.mRequested.size());
399 
400         // [2] WiFi connects but not validated/promoted to default -> mobile selected.
401         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
402         wifiAgent.fakeConnect();
403         mLooper.dispatchAll();
404         assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
405         assertEquals(0, mCM.mRequested.size());
406 
407         // [3] WiFi validates and is promoted to the default network -> WiFi selected.
408         mCM.makeDefaultNetwork(wifiAgent);
409         mLooper.dispatchAll();
410         assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
411         assertEquals(0, mCM.mRequested.size());
412 
413         // [4] DUN required, no other changes -> WiFi still selected
414         mUNM.setUpstreamConfig(false /* autoUpstream */, true /* dunRequired */);
415         assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
416         assertEquals(1, mCM.mRequested.size());
417         assertTrue(isDunRequested());
418 
419         // [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
420         mCM.makeDefaultNetwork(cellAgent);
421         mLooper.dispatchAll();
422         assertEquals(null, mUNM.getCurrentPreferredUpstream());
423         assertEquals(1, mCM.mRequested.size());
424         assertTrue(isDunRequested());
425 
426         // [6] DUN network arrives -> DUN selected
427         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
428         dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
429         dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
430         dunAgent.fakeConnect();
431         mLooper.dispatchAll();
432         assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
433         assertEquals(1, mCM.mRequested.size());
434 
435         // [7] Mobile is not permitted -> null selected
436         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
437         assertEquals(null, mUNM.getCurrentPreferredUpstream());
438         assertEquals(1, mCM.mRequested.size());
439 
440         // [7] Mobile is permitted again -> DUN selected
441         when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
442         assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
443         assertEquals(1, mCM.mRequested.size());
444 
445         // [8] DUN no longer required -> request is withdrawn
446         mUNM.setUpstreamConfig(true /* autoUpstream */, false /* dunRequired */);
447         assertEquals(0, mCM.mRequested.size());
448         assertFalse(isDunRequested());
449     }
450 
451     @Test
testLocalPrefixes()452     public void testLocalPrefixes() throws Exception {
453         mUNM.startTrackDefaultNetwork(mEntitleMgr);
454         mUNM.startObserveAllNetworks();
455 
456         // [0] Test minimum set of local prefixes.
457         Set<IpPrefix> local = mUNM.getLocalPrefixes();
458         assertTrue(local.isEmpty());
459 
460         final Set<String> alreadySeen = new HashSet<>();
461 
462         // [1] Pretend Wi-Fi connects.
463         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
464         final LinkProperties wifiLp = wifiAgent.linkProperties;
465         wifiLp.setInterfaceName("wlan0");
466         final String[] wifi_addrs = {
467                 "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
468                 "2001:db8:4:fd00:827a:bfff:fe6f:374d",
469                 "2001:db8:4:fd00:6dea:325a:fdae:4ef4",
470                 "fd6a:a640:60bf:e985::123",  // ULA address for good measure.
471         };
472         for (String addrStr : wifi_addrs) {
473             final String cidr = addrStr.contains(":") ? "/64" : "/20";
474             wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
475         }
476         wifiAgent.fakeConnect();
477         wifiAgent.sendLinkProperties();
478         mLooper.dispatchAll();
479 
480         local = mUNM.getLocalPrefixes();
481         assertPrefixSet(local, INCLUDES, alreadySeen);
482         final String[] wifiLinkPrefixes = {
483                 // Link-local prefixes are excluded and dealt with elsewhere.
484                 "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
485         };
486         assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
487         Collections.addAll(alreadySeen, wifiLinkPrefixes);
488         assertEquals(alreadySeen.size(), local.size());
489 
490         // [2] Pretend mobile connects.
491         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
492         final LinkProperties cellLp = cellAgent.linkProperties;
493         cellLp.setInterfaceName("rmnet_data0");
494         final String[] cell_addrs = {
495                 "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
496         };
497         for (String addrStr : cell_addrs) {
498             final String cidr = addrStr.contains(":") ? "/64" : "/27";
499             cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
500         }
501         cellAgent.fakeConnect();
502         cellAgent.sendLinkProperties();
503         mLooper.dispatchAll();
504 
505         local = mUNM.getLocalPrefixes();
506         assertPrefixSet(local, INCLUDES, alreadySeen);
507         final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
508         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
509         Collections.addAll(alreadySeen, cellLinkPrefixes);
510         assertEquals(alreadySeen.size(), local.size());
511 
512         // [3] Pretend DUN connects.
513         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, DUN_CAPABILITIES);
514         final LinkProperties dunLp = dunAgent.linkProperties;
515         dunLp.setInterfaceName("rmnet_data1");
516         final String[] dun_addrs = {
517                 "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
518         };
519         for (String addrStr : dun_addrs) {
520             final String cidr = addrStr.contains(":") ? "/64" : "/27";
521             dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
522         }
523         dunAgent.fakeConnect();
524         dunAgent.sendLinkProperties();
525         mLooper.dispatchAll();
526 
527         local = mUNM.getLocalPrefixes();
528         assertPrefixSet(local, INCLUDES, alreadySeen);
529         final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
530         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
531         Collections.addAll(alreadySeen, dunLinkPrefixes);
532         assertEquals(alreadySeen.size(), local.size());
533 
534         // [4] Pretend Wi-Fi disconnected.  It's addresses/prefixes should no
535         // longer be included (should be properly removed).
536         wifiAgent.fakeDisconnect();
537         mLooper.dispatchAll();
538         local = mUNM.getLocalPrefixes();
539         assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
540         assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
541         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
542 
543         // [5] Pretend mobile disconnected.
544         cellAgent.fakeDisconnect();
545         mLooper.dispatchAll();
546         local = mUNM.getLocalPrefixes();
547         assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
548         assertPrefixSet(local, EXCLUDES, cellLinkPrefixes);
549         assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
550 
551         // [6] Pretend DUN disconnected.
552         dunAgent.fakeDisconnect();
553         mLooper.dispatchAll();
554         local = mUNM.getLocalPrefixes();
555         assertTrue(local.isEmpty());
556     }
557 
558     @Test
testSelectMobileWhenMobileIsNotDefault()559     public void testSelectMobileWhenMobileIsNotDefault() {
560         final Collection<Integer> preferredTypes = new ArrayList<>();
561         // Mobile has higher pirority than wifi.
562         preferredTypes.add(TYPE_MOBILE_HIPRI);
563         preferredTypes.add(TYPE_WIFI);
564         mUNM.startTrackDefaultNetwork(mEntitleMgr);
565         mUNM.startObserveAllNetworks();
566         // Setup wifi and make wifi as default network.
567         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
568         wifiAgent.fakeConnect();
569         mCM.makeDefaultNetwork(wifiAgent);
570         // Setup mobile network.
571         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
572         cellAgent.fakeConnect();
573         mLooper.dispatchAll();
574 
575         assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
576                 mUNM.selectPreferredUpstreamType(preferredTypes));
577         verify(mEntitleMgr, times(1)).maybeRunProvisioning();
578     }
579 
assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns)580     private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) {
581         if (legacyType == TYPE_NONE) {
582             assertTrue(ns == null);
583             return;
584         }
585 
586         final NetworkCapabilities nc =
587                 UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType);
588         assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
589     }
590 
assertUpstreamTypeRequested(int upstreamType)591     private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
592         assertEquals(1, mCM.mRequested.size());
593         assertEquals(1, mCM.mLegacyTypeMap.size());
594         assertEquals(Integer.valueOf(upstreamType),
595                 mCM.mLegacyTypeMap.values().iterator().next());
596     }
597 
isDunRequested()598     private boolean isDunRequested() {
599         for (NetworkRequestInfo nri : mCM.mRequested.values()) {
600             if (nri.request.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
601                 return true;
602             }
603         }
604         return false;
605     }
606 
607     public static class TestStateMachine extends StateMachine {
608         public final ArrayList<Message> messages = new ArrayList<>();
609         private final State mLoggingState = new LoggingState();
610 
611         class LoggingState extends State {
enter()612             @Override public void enter() {
613                 messages.clear();
614             }
615 
exit()616             @Override public void exit() {
617                 messages.clear();
618             }
619 
processMessage(Message msg)620             @Override public boolean processMessage(Message msg) {
621                 messages.add(msg);
622                 return true;
623             }
624         }
625 
TestStateMachine(Looper looper)626         public TestStateMachine(Looper looper) {
627             super("UpstreamNetworkMonitor.TestStateMachine", looper);
628             addState(mLoggingState);
629             setInitialState(mLoggingState);
630             super.start();
631         }
632     }
633 
assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected)634     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
635         final Set<String> expectedSet = new HashSet<>();
636         Collections.addAll(expectedSet, expected);
637         assertPrefixSet(prefixes, expectation, expectedSet);
638     }
639 
assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected)640     static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
641         for (String expectedPrefix : expected) {
642             final String errStr = expectation ? "did not find" : "found";
643             assertEquals(
644                     String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
645                     expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
646         }
647     }
648 }
649