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.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertFalse; 31 import static org.junit.Assert.assertTrue; 32 import static org.junit.Assert.fail; 33 import static org.mockito.Mockito.any; 34 import static org.mockito.Mockito.anyInt; 35 import static org.mockito.Mockito.anyString; 36 import static org.mockito.Mockito.eq; 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; 46 import android.net.ConnectivityManager.NetworkCallback; 47 import android.net.IConnectivityManager; 48 import android.net.IpPrefix; 49 import android.net.LinkAddress; 50 import android.net.LinkProperties; 51 import android.net.Network; 52 import android.net.NetworkCapabilities; 53 import android.net.NetworkRequest; 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 sDefaultRequest = 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(sDefaultRequest, 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(sDefaultRequest, 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(sDefaultRequest, mEntitleMgr); 168 verify(mCM, times(1)).requestNetwork( 169 eq(sDefaultRequest), 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(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), anyInt(), anyInt(), any(Handler.class), 216 any(NetworkCallback.class)); 217 218 assertTrue(mUNM.mobileNetworkRequested()); 219 assertUpstreamTypeRequested(TYPE_MOBILE_DUN); 220 assertTrue(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(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(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(isDunRequested()); 270 mUNM.updateMobileRequiresDun(true); 271 assertTrue(mUNM.mobileNetworkRequested()); 272 assertUpstreamTypeRequested(TYPE_MOBILE_DUN); 273 assertTrue(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(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(sDefaultRequest, 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(sDefaultRequest, 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(sDefaultRequest, 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(sDefaultRequest, 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 } 542 assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns)543 private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) { 544 if (legacyType == TYPE_NONE) { 545 assertTrue(ns == null); 546 return; 547 } 548 549 final NetworkCapabilities nc = 550 UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType); 551 assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities)); 552 } 553 assertUpstreamTypeRequested(int upstreamType)554 private void assertUpstreamTypeRequested(int upstreamType) throws Exception { 555 assertEquals(1, mCM.requested.size()); 556 assertEquals(1, mCM.legacyTypeMap.size()); 557 assertEquals(Integer.valueOf(upstreamType), 558 mCM.legacyTypeMap.values().iterator().next()); 559 } 560 isDunRequested()561 private boolean isDunRequested() { 562 for (NetworkRequest req : mCM.requested.values()) { 563 if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { 564 return true; 565 } 566 } 567 return false; 568 } 569 570 public static class TestConnectivityManager extends ConnectivityManager { 571 public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>(); 572 public Set<NetworkCallback> trackingDefault = new HashSet<>(); 573 public TestNetworkAgent defaultNetwork = null; 574 public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>(); 575 public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>(); 576 public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>(); 577 578 private int mNetworkId = 100; 579 TestConnectivityManager(Context ctx, IConnectivityManager svc)580 public TestConnectivityManager(Context ctx, IConnectivityManager svc) { 581 super(ctx, svc); 582 } 583 hasNoCallbacks()584 boolean hasNoCallbacks() { 585 return allCallbacks.isEmpty() 586 && trackingDefault.isEmpty() 587 && listening.isEmpty() 588 && requested.isEmpty() 589 && legacyTypeMap.isEmpty(); 590 } 591 onlyHasDefaultCallbacks()592 boolean onlyHasDefaultCallbacks() { 593 return (allCallbacks.size() == 1) 594 && (trackingDefault.size() == 1) 595 && listening.isEmpty() 596 && requested.isEmpty() 597 && legacyTypeMap.isEmpty(); 598 } 599 isListeningForAll()600 boolean isListeningForAll() { 601 final NetworkCapabilities empty = new NetworkCapabilities(); 602 empty.clearAll(); 603 604 for (NetworkRequest req : listening.values()) { 605 if (req.networkCapabilities.equalRequestableCapabilities(empty)) { 606 return true; 607 } 608 } 609 return false; 610 } 611 getNetworkId()612 int getNetworkId() { 613 return ++mNetworkId; 614 } 615 makeDefaultNetwork(TestNetworkAgent agent)616 void makeDefaultNetwork(TestNetworkAgent agent) { 617 if (Objects.equals(defaultNetwork, agent)) return; 618 619 final TestNetworkAgent formerDefault = defaultNetwork; 620 defaultNetwork = agent; 621 622 for (NetworkCallback cb : trackingDefault) { 623 if (defaultNetwork != null) { 624 cb.onAvailable(defaultNetwork.networkId); 625 cb.onCapabilitiesChanged( 626 defaultNetwork.networkId, defaultNetwork.networkCapabilities); 627 cb.onLinkPropertiesChanged( 628 defaultNetwork.networkId, defaultNetwork.linkProperties); 629 } 630 } 631 } 632 633 @Override requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h)634 public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { 635 assertFalse(allCallbacks.containsKey(cb)); 636 allCallbacks.put(cb, h); 637 if (sDefaultRequest.equals(req)) { 638 assertFalse(trackingDefault.contains(cb)); 639 trackingDefault.add(cb); 640 } else { 641 assertFalse(requested.containsKey(cb)); 642 requested.put(cb, req); 643 } 644 } 645 646 @Override requestNetwork(NetworkRequest req, NetworkCallback cb)647 public void requestNetwork(NetworkRequest req, NetworkCallback cb) { 648 fail("Should never be called."); 649 } 650 651 @Override requestNetwork(NetworkRequest req, int timeoutMs, int legacyType, Handler h, NetworkCallback cb)652 public void requestNetwork(NetworkRequest req, 653 int timeoutMs, int legacyType, Handler h, NetworkCallback cb) { 654 assertFalse(allCallbacks.containsKey(cb)); 655 allCallbacks.put(cb, h); 656 assertFalse(requested.containsKey(cb)); 657 requested.put(cb, req); 658 assertFalse(legacyTypeMap.containsKey(cb)); 659 if (legacyType != ConnectivityManager.TYPE_NONE) { 660 legacyTypeMap.put(cb, legacyType); 661 } 662 } 663 664 @Override registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h)665 public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) { 666 assertFalse(allCallbacks.containsKey(cb)); 667 allCallbacks.put(cb, h); 668 assertFalse(listening.containsKey(cb)); 669 listening.put(cb, req); 670 } 671 672 @Override registerNetworkCallback(NetworkRequest req, NetworkCallback cb)673 public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) { 674 fail("Should never be called."); 675 } 676 677 @Override registerDefaultNetworkCallback(NetworkCallback cb, Handler h)678 public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) { 679 fail("Should never be called."); 680 } 681 682 @Override registerDefaultNetworkCallback(NetworkCallback cb)683 public void registerDefaultNetworkCallback(NetworkCallback cb) { 684 fail("Should never be called."); 685 } 686 687 @Override unregisterNetworkCallback(NetworkCallback cb)688 public void unregisterNetworkCallback(NetworkCallback cb) { 689 if (trackingDefault.contains(cb)) { 690 trackingDefault.remove(cb); 691 } else if (listening.containsKey(cb)) { 692 listening.remove(cb); 693 } else if (requested.containsKey(cb)) { 694 requested.remove(cb); 695 legacyTypeMap.remove(cb); 696 } else { 697 fail("Unexpected callback removed"); 698 } 699 allCallbacks.remove(cb); 700 701 assertFalse(allCallbacks.containsKey(cb)); 702 assertFalse(trackingDefault.contains(cb)); 703 assertFalse(listening.containsKey(cb)); 704 assertFalse(requested.containsKey(cb)); 705 } 706 } 707 708 public static class TestNetworkAgent { 709 public final TestConnectivityManager cm; 710 public final Network networkId; 711 public final int transportType; 712 public final NetworkCapabilities networkCapabilities; 713 public final LinkProperties linkProperties; 714 TestNetworkAgent(TestConnectivityManager cm, int transportType)715 public TestNetworkAgent(TestConnectivityManager cm, int transportType) { 716 this.cm = cm; 717 this.networkId = new Network(cm.getNetworkId()); 718 this.transportType = transportType; 719 networkCapabilities = new NetworkCapabilities(); 720 networkCapabilities.addTransportType(transportType); 721 networkCapabilities.addCapability(NET_CAPABILITY_INTERNET); 722 linkProperties = new LinkProperties(); 723 } 724 fakeConnect()725 public void fakeConnect() { 726 for (NetworkCallback cb : cm.listening.keySet()) { 727 cb.onAvailable(networkId); 728 cb.onCapabilitiesChanged(networkId, copy(networkCapabilities)); 729 cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); 730 } 731 } 732 fakeDisconnect()733 public void fakeDisconnect() { 734 for (NetworkCallback cb : cm.listening.keySet()) { 735 cb.onLost(networkId); 736 } 737 } 738 sendLinkProperties()739 public void sendLinkProperties() { 740 for (NetworkCallback cb : cm.listening.keySet()) { 741 cb.onLinkPropertiesChanged(networkId, copy(linkProperties)); 742 } 743 } 744 745 @Override toString()746 public String toString() { 747 return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities); 748 } 749 } 750 751 public static class TestStateMachine extends StateMachine { 752 public final ArrayList<Message> messages = new ArrayList<>(); 753 private final State mLoggingState = new LoggingState(); 754 755 class LoggingState extends State { enter()756 @Override public void enter() { 757 messages.clear(); 758 } 759 exit()760 @Override public void exit() { 761 messages.clear(); 762 } 763 processMessage(Message msg)764 @Override public boolean processMessage(Message msg) { 765 messages.add(msg); 766 return true; 767 } 768 } 769 TestStateMachine()770 public TestStateMachine() { 771 super("UpstreamNetworkMonitor.TestStateMachine"); 772 addState(mLoggingState); 773 setInitialState(mLoggingState); 774 super.start(); 775 } 776 } 777 copy(NetworkCapabilities nc)778 static NetworkCapabilities copy(NetworkCapabilities nc) { 779 return new NetworkCapabilities(nc); 780 } 781 copy(LinkProperties lp)782 static LinkProperties copy(LinkProperties lp) { 783 return new LinkProperties(lp); 784 } 785 assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected)786 static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) { 787 final Set<String> expectedSet = new HashSet<>(); 788 Collections.addAll(expectedSet, expected); 789 assertPrefixSet(prefixes, expectation, expectedSet); 790 } 791 assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected)792 static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) { 793 for (String expectedPrefix : expected) { 794 final String errStr = expectation ? "did not find" : "found"; 795 assertEquals( 796 String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix), 797 expectation, prefixes.contains(new IpPrefix(expectedPrefix))); 798 } 799 } 800 } 801