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