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