1 /* 2 * Copyright (C) 2016 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 android.net.ip; 18 19 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; 20 import static android.net.ConnectivityManager.TETHERING_USB; 21 import static android.net.ConnectivityManager.TETHERING_WIFI; 22 import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; 23 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; 24 import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; 25 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; 26 import static android.net.ip.IpServer.STATE_AVAILABLE; 27 import static android.net.ip.IpServer.STATE_TETHERED; 28 import static android.net.ip.IpServer.STATE_UNAVAILABLE; 29 import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; 30 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertFalse; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assert.fail; 36 import static org.mockito.ArgumentMatchers.argThat; 37 import static org.mockito.Matchers.any; 38 import static org.mockito.Matchers.anyString; 39 import static org.mockito.Matchers.eq; 40 import static org.mockito.Mockito.doAnswer; 41 import static org.mockito.Mockito.doThrow; 42 import static org.mockito.Mockito.inOrder; 43 import static org.mockito.Mockito.never; 44 import static org.mockito.Mockito.reset; 45 import static org.mockito.Mockito.timeout; 46 import static org.mockito.Mockito.times; 47 import static org.mockito.Mockito.verify; 48 import static org.mockito.Mockito.verifyNoMoreInteractions; 49 import static org.mockito.Mockito.when; 50 51 import android.net.INetd; 52 import android.net.INetworkStatsService; 53 import android.net.InterfaceConfiguration; 54 import android.net.IpPrefix; 55 import android.net.LinkAddress; 56 import android.net.LinkProperties; 57 import android.net.MacAddress; 58 import android.net.RouteInfo; 59 import android.net.dhcp.DhcpServingParamsParcel; 60 import android.net.dhcp.IDhcpServer; 61 import android.net.dhcp.IDhcpServerCallbacks; 62 import android.net.util.InterfaceParams; 63 import android.net.util.InterfaceSet; 64 import android.net.util.SharedLog; 65 import android.os.INetworkManagementService; 66 import android.os.RemoteException; 67 import android.os.test.TestLooper; 68 import android.text.TextUtils; 69 70 import androidx.test.filters.SmallTest; 71 import androidx.test.runner.AndroidJUnit4; 72 73 import org.junit.Before; 74 import org.junit.Test; 75 import org.junit.runner.RunWith; 76 import org.mockito.ArgumentCaptor; 77 import org.mockito.Captor; 78 import org.mockito.InOrder; 79 import org.mockito.Mock; 80 import org.mockito.MockitoAnnotations; 81 82 import java.net.Inet4Address; 83 84 @RunWith(AndroidJUnit4.class) 85 @SmallTest 86 public class IpServerTest { 87 private static final String IFACE_NAME = "testnet1"; 88 private static final String UPSTREAM_IFACE = "upstream0"; 89 private static final String UPSTREAM_IFACE2 = "upstream1"; 90 private static final int DHCP_LEASE_TIME_SECS = 3600; 91 92 private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( 93 IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); 94 95 private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; 96 97 @Mock private INetworkManagementService mNMService; 98 @Mock private INetd mNetd; 99 @Mock private INetworkStatsService mStatsService; 100 @Mock private IpServer.Callback mCallback; 101 @Mock private InterfaceConfiguration mInterfaceConfiguration; 102 @Mock private SharedLog mSharedLog; 103 @Mock private IDhcpServer mDhcpServer; 104 @Mock private RouterAdvertisementDaemon mRaDaemon; 105 @Mock private IpServer.Dependencies mDependencies; 106 107 @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor; 108 109 private final TestLooper mLooper = new TestLooper(); 110 private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor = 111 ArgumentCaptor.forClass(LinkProperties.class); 112 private IpServer mIpServer; 113 initStateMachine(int interfaceType)114 private void initStateMachine(int interfaceType) throws Exception { 115 initStateMachine(interfaceType, false /* usingLegacyDhcp */); 116 } 117 initStateMachine(int interfaceType, boolean usingLegacyDhcp)118 private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception { 119 doAnswer(inv -> { 120 final IDhcpServerCallbacks cb = inv.getArgument(2); 121 new Thread(() -> { 122 try { 123 cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer); 124 } catch (RemoteException e) { 125 fail(e.getMessage()); 126 } 127 }).run(); 128 return null; 129 }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); 130 when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); 131 when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); 132 when(mDependencies.getNetdService()).thenReturn(mNetd); 133 134 mIpServer = new IpServer( 135 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, 136 mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies); 137 mIpServer.start(); 138 // Starting the state machine always puts us in a consistent state and notifies 139 // the rest of the world that we've changed from an unknown to available state. 140 mLooper.dispatchAll(); 141 reset(mNMService, mStatsService, mCallback); 142 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); 143 144 when(mRaDaemon.start()).thenReturn(true); 145 } 146 initTetheredStateMachine(int interfaceType, String upstreamIface)147 private void initTetheredStateMachine(int interfaceType, String upstreamIface) 148 throws Exception { 149 initTetheredStateMachine(interfaceType, upstreamIface, false); 150 } 151 initTetheredStateMachine(int interfaceType, String upstreamIface, boolean usingLegacyDhcp)152 private void initTetheredStateMachine(int interfaceType, String upstreamIface, 153 boolean usingLegacyDhcp) throws Exception { 154 initStateMachine(interfaceType, usingLegacyDhcp); 155 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); 156 if (upstreamIface != null) { 157 dispatchTetherConnectionChanged(upstreamIface); 158 } 159 reset(mNMService, mStatsService, mCallback); 160 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); 161 } 162 setUp()163 @Before public void setUp() throws Exception { 164 MockitoAnnotations.initMocks(this); 165 when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); 166 } 167 168 @Test startsOutAvailable()169 public void startsOutAvailable() { 170 mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), 171 TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback, 172 false /* usingLegacyDhcp */, mDependencies); 173 mIpServer.start(); 174 mLooper.dispatchAll(); 175 verify(mCallback).updateInterfaceState( 176 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); 177 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); 178 verifyNoMoreInteractions(mCallback, mNMService, mStatsService); 179 } 180 181 @Test shouldDoNothingUntilRequested()182 public void shouldDoNothingUntilRequested() throws Exception { 183 initStateMachine(TETHERING_BLUETOOTH); 184 final int [] NOOP_COMMANDS = { 185 IpServer.CMD_TETHER_UNREQUESTED, 186 IpServer.CMD_IP_FORWARDING_ENABLE_ERROR, 187 IpServer.CMD_IP_FORWARDING_DISABLE_ERROR, 188 IpServer.CMD_START_TETHERING_ERROR, 189 IpServer.CMD_STOP_TETHERING_ERROR, 190 IpServer.CMD_SET_DNS_FORWARDERS_ERROR, 191 IpServer.CMD_TETHER_CONNECTION_CHANGED 192 }; 193 for (int command : NOOP_COMMANDS) { 194 // None of these commands should trigger us to request action from 195 // the rest of the system. 196 dispatchCommand(command); 197 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 198 } 199 } 200 201 @Test handlesImmediateInterfaceDown()202 public void handlesImmediateInterfaceDown() throws Exception { 203 initStateMachine(TETHERING_BLUETOOTH); 204 205 dispatchCommand(IpServer.CMD_INTERFACE_DOWN); 206 verify(mCallback).updateInterfaceState( 207 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); 208 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); 209 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 210 } 211 212 @Test canBeTethered()213 public void canBeTethered() throws Exception { 214 initStateMachine(TETHERING_BLUETOOTH); 215 216 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); 217 InOrder inOrder = inOrder(mCallback, mNMService); 218 inOrder.verify(mNMService).tetherInterface(IFACE_NAME); 219 inOrder.verify(mCallback).updateInterfaceState( 220 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); 221 inOrder.verify(mCallback).updateLinkProperties( 222 eq(mIpServer), any(LinkProperties.class)); 223 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 224 } 225 226 @Test canUnrequestTethering()227 public void canUnrequestTethering() throws Exception { 228 initTetheredStateMachine(TETHERING_BLUETOOTH, null); 229 230 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); 231 InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); 232 inOrder.verify(mNMService).untetherInterface(IFACE_NAME); 233 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); 234 inOrder.verify(mCallback).updateInterfaceState( 235 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); 236 inOrder.verify(mCallback).updateLinkProperties( 237 eq(mIpServer), any(LinkProperties.class)); 238 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 239 } 240 241 @Test canBeTetheredAsUsb()242 public void canBeTetheredAsUsb() throws Exception { 243 initStateMachine(TETHERING_USB); 244 245 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); 246 InOrder inOrder = inOrder(mCallback, mNMService); 247 inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); 248 inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); 249 inOrder.verify(mNMService).tetherInterface(IFACE_NAME); 250 inOrder.verify(mCallback).updateInterfaceState( 251 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); 252 inOrder.verify(mCallback).updateLinkProperties( 253 eq(mIpServer), mLinkPropertiesCaptor.capture()); 254 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); 255 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 256 } 257 258 @Test handlesFirstUpstreamChange()259 public void handlesFirstUpstreamChange() throws Exception { 260 initTetheredStateMachine(TETHERING_BLUETOOTH, null); 261 262 // Telling the state machine about its upstream interface triggers 263 // a little more configuration. 264 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 265 InOrder inOrder = inOrder(mNMService); 266 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE); 267 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); 268 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 269 } 270 271 @Test handlesChangingUpstream()272 public void handlesChangingUpstream() throws Exception { 273 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); 274 275 dispatchTetherConnectionChanged(UPSTREAM_IFACE2); 276 InOrder inOrder = inOrder(mNMService, mStatsService); 277 inOrder.verify(mStatsService).forceUpdate(); 278 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); 279 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); 280 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); 281 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); 282 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 283 } 284 285 @Test handlesChangingUpstreamNatFailure()286 public void handlesChangingUpstreamNatFailure() throws Exception { 287 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); 288 289 doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); 290 291 dispatchTetherConnectionChanged(UPSTREAM_IFACE2); 292 InOrder inOrder = inOrder(mNMService, mStatsService); 293 inOrder.verify(mStatsService).forceUpdate(); 294 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); 295 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); 296 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); 297 inOrder.verify(mStatsService).forceUpdate(); 298 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); 299 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); 300 } 301 302 @Test handlesChangingUpstreamInterfaceForwardingFailure()303 public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { 304 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); 305 306 doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding( 307 IFACE_NAME, UPSTREAM_IFACE2); 308 309 dispatchTetherConnectionChanged(UPSTREAM_IFACE2); 310 InOrder inOrder = inOrder(mNMService, mStatsService); 311 inOrder.verify(mStatsService).forceUpdate(); 312 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); 313 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); 314 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); 315 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); 316 inOrder.verify(mStatsService).forceUpdate(); 317 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); 318 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); 319 } 320 321 @Test canUnrequestTetheringWithUpstream()322 public void canUnrequestTetheringWithUpstream() throws Exception { 323 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); 324 325 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); 326 InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback); 327 inOrder.verify(mStatsService).forceUpdate(); 328 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); 329 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); 330 inOrder.verify(mNMService).untetherInterface(IFACE_NAME); 331 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); 332 inOrder.verify(mCallback).updateInterfaceState( 333 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); 334 inOrder.verify(mCallback).updateLinkProperties( 335 eq(mIpServer), any(LinkProperties.class)); 336 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 337 } 338 339 @Test interfaceDownLeadsToUnavailable()340 public void interfaceDownLeadsToUnavailable() throws Exception { 341 for (boolean shouldThrow : new boolean[]{true, false}) { 342 initTetheredStateMachine(TETHERING_USB, null); 343 344 if (shouldThrow) { 345 doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); 346 } 347 dispatchCommand(IpServer.CMD_INTERFACE_DOWN); 348 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); 349 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); 350 usbTeardownOrder.verify(mNMService).setInterfaceConfig( 351 IFACE_NAME, mInterfaceConfiguration); 352 usbTeardownOrder.verify(mCallback).updateInterfaceState( 353 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); 354 usbTeardownOrder.verify(mCallback).updateLinkProperties( 355 eq(mIpServer), mLinkPropertiesCaptor.capture()); 356 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); 357 } 358 } 359 360 @Test usbShouldBeTornDownOnTetherError()361 public void usbShouldBeTornDownOnTetherError() throws Exception { 362 initStateMachine(TETHERING_USB); 363 364 doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); 365 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); 366 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); 367 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); 368 usbTeardownOrder.verify(mNMService).setInterfaceConfig( 369 IFACE_NAME, mInterfaceConfiguration); 370 usbTeardownOrder.verify(mCallback).updateInterfaceState( 371 mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); 372 usbTeardownOrder.verify(mCallback).updateLinkProperties( 373 eq(mIpServer), mLinkPropertiesCaptor.capture()); 374 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); 375 } 376 377 @Test shouldTearDownUsbOnUpstreamError()378 public void shouldTearDownUsbOnUpstreamError() throws Exception { 379 initTetheredStateMachine(TETHERING_USB, null); 380 381 doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); 382 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 383 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); 384 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); 385 usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); 386 usbTeardownOrder.verify(mCallback).updateInterfaceState( 387 mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); 388 usbTeardownOrder.verify(mCallback).updateLinkProperties( 389 eq(mIpServer), mLinkPropertiesCaptor.capture()); 390 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); 391 } 392 393 @Test ignoresDuplicateUpstreamNotifications()394 public void ignoresDuplicateUpstreamNotifications() throws Exception { 395 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); 396 397 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 398 399 for (int i = 0; i < 5; i++) { 400 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 401 verifyNoMoreInteractions(mNMService, mStatsService, mCallback); 402 } 403 } 404 405 @Test startsDhcpServer()406 public void startsDhcpServer() throws Exception { 407 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); 408 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 409 410 assertDhcpStarted(new IpPrefix("192.168.43.0/24")); 411 } 412 413 @Test startsDhcpServerOnBluetooth()414 public void startsDhcpServerOnBluetooth() throws Exception { 415 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); 416 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 417 418 assertDhcpStarted(new IpPrefix("192.168.44.0/24")); 419 } 420 421 @Test doesNotStartDhcpServerIfDisabled()422 public void doesNotStartDhcpServerIfDisabled() throws Exception { 423 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */); 424 dispatchTetherConnectionChanged(UPSTREAM_IFACE); 425 426 verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); 427 } 428 assertDhcpStarted(IpPrefix expectedPrefix)429 private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { 430 verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); 431 verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any()); 432 final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue(); 433 // Last address byte is random 434 assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr))); 435 assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength); 436 assertEquals(1, params.defaultRouters.length); 437 assertEquals(params.serverAddr, params.defaultRouters[0]); 438 assertEquals(1, params.dnsServers.length); 439 assertEquals(params.serverAddr, params.dnsServers[0]); 440 assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); 441 } 442 443 /** 444 * Send a command to the state machine under test, and run the event loop to idle. 445 * 446 * @param command One of the IpServer.CMD_* constants. 447 * @param arg1 An additional argument to pass. 448 */ dispatchCommand(int command, int arg1)449 private void dispatchCommand(int command, int arg1) { 450 mIpServer.sendMessage(command, arg1); 451 mLooper.dispatchAll(); 452 } 453 454 /** 455 * Send a command to the state machine under test, and run the event loop to idle. 456 * 457 * @param command One of the IpServer.CMD_* constants. 458 */ dispatchCommand(int command)459 private void dispatchCommand(int command) { 460 mIpServer.sendMessage(command); 461 mLooper.dispatchAll(); 462 } 463 464 /** 465 * Special override to tell the state machine that the upstream interface has changed. 466 * 467 * @see #dispatchCommand(int) 468 * @param upstreamIface String name of upstream interface (or null) 469 */ dispatchTetherConnectionChanged(String upstreamIface)470 private void dispatchTetherConnectionChanged(String upstreamIface) { 471 mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, 472 new InterfaceSet(upstreamIface)); 473 mLooper.dispatchAll(); 474 } 475 assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp)476 private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) { 477 // Find the first IPv4 LinkAddress. 478 LinkAddress addr4 = null; 479 for (LinkAddress addr : lp.getLinkAddresses()) { 480 if (!(addr.getAddress() instanceof Inet4Address)) continue; 481 addr4 = addr; 482 break; 483 } 484 assertNotNull("missing IPv4 address", addr4); 485 486 // Assert the presence of the associated directly connected route. 487 final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName()); 488 assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'", 489 lp.getRoutes().contains(directlyConnected)); 490 } 491 assertNoAddressesNorRoutes(LinkProperties lp)492 private void assertNoAddressesNorRoutes(LinkProperties lp) { 493 assertTrue(lp.getLinkAddresses().isEmpty()); 494 assertTrue(lp.getRoutes().isEmpty()); 495 // We also check that interface name is non-empty, because we should 496 // never see an empty interface name in any LinkProperties update. 497 assertFalse(TextUtils.isEmpty(lp.getInterfaceName())); 498 } 499 } 500