1 /* 2 * Copyright (C) 2020 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; 18 19 import static android.Manifest.permission.MANAGE_TEST_NETWORKS; 20 import static android.Manifest.permission.NETWORK_SETTINGS; 21 import static android.Manifest.permission.TETHER_PRIVILEGED; 22 import static android.net.TetheringManager.TETHERING_ETHERNET; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 import static org.junit.Assume.assumeFalse; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.app.UiAutomation; 33 import android.content.Context; 34 import android.net.EthernetManager.TetheredInterfaceCallback; 35 import android.net.EthernetManager.TetheredInterfaceRequest; 36 import android.net.TetheringManager.StartTetheringCallback; 37 import android.net.TetheringManager.TetheringEventCallback; 38 import android.net.TetheringManager.TetheringRequest; 39 import android.net.dhcp.DhcpAckPacket; 40 import android.net.dhcp.DhcpOfferPacket; 41 import android.net.dhcp.DhcpPacket; 42 import android.os.Handler; 43 import android.os.HandlerThread; 44 import android.os.SystemClock; 45 import android.os.SystemProperties; 46 import android.system.Os; 47 import android.util.Log; 48 49 import androidx.test.InstrumentationRegistry; 50 import androidx.test.filters.MediumTest; 51 import androidx.test.runner.AndroidJUnit4; 52 53 import com.android.testutils.HandlerUtilsKt; 54 import com.android.testutils.TapPacketReader; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 61 import java.io.FileDescriptor; 62 import java.net.Inet4Address; 63 import java.net.InterfaceAddress; 64 import java.net.NetworkInterface; 65 import java.net.SocketException; 66 import java.nio.ByteBuffer; 67 import java.util.Collection; 68 import java.util.List; 69 import java.util.Random; 70 import java.util.concurrent.CompletableFuture; 71 import java.util.concurrent.CountDownLatch; 72 import java.util.concurrent.TimeUnit; 73 import java.util.concurrent.TimeoutException; 74 75 @RunWith(AndroidJUnit4.class) 76 @MediumTest 77 public class EthernetTetheringTest { 78 79 private static final String TAG = EthernetTetheringTest.class.getSimpleName(); 80 private static final int TIMEOUT_MS = 5000; 81 private static final int PACKET_READ_TIMEOUT_MS = 100; 82 private static final int DHCP_DISCOVER_ATTEMPTS = 10; 83 private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { 84 DhcpPacket.DHCP_SUBNET_MASK, 85 DhcpPacket.DHCP_ROUTER, 86 DhcpPacket.DHCP_DNS_SERVER, 87 DhcpPacket.DHCP_LEASE_TIME, 88 }; 89 private static final String DHCP_HOSTNAME = "testhostname"; 90 91 private final Context mContext = InstrumentationRegistry.getContext(); 92 private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); 93 private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); 94 95 private TestNetworkInterface mTestIface; 96 private HandlerThread mHandlerThread; 97 private Handler mHandler; 98 private TapPacketReader mTapPacketReader; 99 100 private TetheredInterfaceRequester mTetheredInterfaceRequester; 101 private MyTetheringEventCallback mTetheringEventCallback; 102 103 private UiAutomation mUiAutomation = 104 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 105 private boolean mRunTests; 106 107 @Before setUp()108 public void setUp() throws Exception { 109 // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive 110 // tethered client callbacks. 111 mUiAutomation.adoptShellPermissionIdentity( 112 MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED); 113 mRunTests = mTm.isTetheringSupported() && mEm != null; 114 assumeTrue(mRunTests); 115 116 mHandlerThread = new HandlerThread(getClass().getSimpleName()); 117 mHandlerThread.start(); 118 mHandler = new Handler(mHandlerThread.getLooper()); 119 mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); 120 } 121 cleanUp()122 private void cleanUp() throws Exception { 123 mTm.stopTethering(TETHERING_ETHERNET); 124 if (mTetheringEventCallback != null) { 125 mTetheringEventCallback.awaitInterfaceUntethered(); 126 mTetheringEventCallback.unregister(); 127 mTetheringEventCallback = null; 128 } 129 if (mTapPacketReader != null) { 130 TapPacketReader reader = mTapPacketReader; 131 mHandler.post(() -> reader.stop()); 132 mTapPacketReader = null; 133 } 134 mHandlerThread.quitSafely(); 135 mTetheredInterfaceRequester.release(); 136 mEm.setIncludeTestInterfaces(false); 137 maybeDeleteTestInterface(); 138 } 139 140 @After tearDown()141 public void tearDown() throws Exception { 142 try { 143 if (mRunTests) cleanUp(); 144 } finally { 145 mUiAutomation.dropShellPermissionIdentity(); 146 } 147 } 148 149 @Test testVirtualEthernetAlreadyExists()150 public void testVirtualEthernetAlreadyExists() throws Exception { 151 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 152 assumeFalse(mEm.isAvailable()); 153 154 mTestIface = createTestInterface(); 155 // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the 156 // interface will be placed in client mode, which will delete the link-local address. 157 // At that point NetworkInterface.getByName() will cease to work on the interface, because 158 // starting in R NetworkInterface can no longer see interfaces without IP addresses. 159 int mtu = getMTU(mTestIface); 160 161 Log.d(TAG, "Including test interfaces"); 162 mEm.setIncludeTestInterfaces(true); 163 164 final String iface = mTetheredInterfaceRequester.getInterface(); 165 assertEquals("TetheredInterfaceCallback for unexpected interface", 166 mTestIface.getInterfaceName(), iface); 167 168 checkVirtualEthernet(mTestIface, mtu); 169 } 170 171 @Test testVirtualEthernet()172 public void testVirtualEthernet() throws Exception { 173 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 174 assumeFalse(mEm.isAvailable()); 175 176 CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface(); 177 178 mEm.setIncludeTestInterfaces(true); 179 180 mTestIface = createTestInterface(); 181 182 final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 183 assertEquals("TetheredInterfaceCallback for unexpected interface", 184 mTestIface.getInterfaceName(), iface); 185 186 checkVirtualEthernet(mTestIface, getMTU(mTestIface)); 187 } 188 189 @Test testStaticIpv4()190 public void testStaticIpv4() throws Exception { 191 assumeFalse(mEm.isAvailable()); 192 193 mEm.setIncludeTestInterfaces(true); 194 195 mTestIface = createTestInterface(); 196 197 final String iface = mTetheredInterfaceRequester.getInterface(); 198 assertEquals("TetheredInterfaceCallback for unexpected interface", 199 mTestIface.getInterfaceName(), iface); 200 201 assertInvalidStaticIpv4Request(iface, null, null); 202 assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); 203 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); 204 assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); 205 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); 206 assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); 207 assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); 208 209 final String localAddr = "192.0.2.3/28"; 210 final String clientAddr = "192.0.2.2/28"; 211 mTetheringEventCallback = enableEthernetTethering(iface, 212 requestWithStaticIpv4(localAddr, clientAddr)); 213 214 mTetheringEventCallback.awaitInterfaceTethered(); 215 assertInterfaceHasIpAddress(iface, localAddr); 216 217 byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); 218 byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); 219 220 FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); 221 mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); 222 DhcpResults dhcpResults = runDhcp(fd, client1); 223 assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); 224 225 try { 226 runDhcp(fd, client2); 227 fail("Only one client should get an IP address"); 228 } catch (TimeoutException expected) { } 229 230 } 231 isAdbOverNetwork()232 private boolean isAdbOverNetwork() { 233 // If adb TCP port opened, this test may running by adb over network. 234 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) 235 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); 236 } 237 238 @Test testPhysicalEthernet()239 public void testPhysicalEthernet() throws Exception { 240 assumeTrue(mEm.isAvailable()); 241 // Do not run this test if adb is over network and ethernet is connected. 242 // It is likely the adb run over ethernet, the adb would break when ethernet is switching 243 // from client mode to server mode. See b/160389275. 244 assumeFalse(isAdbOverNetwork()); 245 246 // Get an interface to use. 247 final String iface = mTetheredInterfaceRequester.getInterface(); 248 249 // Enable Ethernet tethering and check that it starts. 250 mTetheringEventCallback = enableEthernetTethering(iface); 251 252 // There is nothing more we can do on a physical interface without connecting an actual 253 // client, which is not possible in this test. 254 } 255 256 private static final class MyTetheringEventCallback implements TetheringEventCallback { 257 private final TetheringManager mTm; 258 private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); 259 private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); 260 private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); 261 private final String mIface; 262 263 private volatile boolean mInterfaceWasTethered = false; 264 private volatile boolean mUnregistered = false; 265 private volatile Collection<TetheredClient> mClients = null; 266 MyTetheringEventCallback(TetheringManager tm, String iface)267 MyTetheringEventCallback(TetheringManager tm, String iface) { 268 mTm = tm; 269 mIface = iface; 270 } 271 unregister()272 public void unregister() { 273 mTm.unregisterTetheringEventCallback(this); 274 mUnregistered = true; 275 } 276 277 @Override onTetheredInterfacesChanged(List<String> interfaces)278 public void onTetheredInterfacesChanged(List<String> interfaces) { 279 // Ignore stale callbacks registered by previous test cases. 280 if (mUnregistered) return; 281 282 final boolean wasTethered = mTetheringStartedLatch.getCount() == 0; 283 if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { 284 // This interface is being tethered for the first time. 285 Log.d(TAG, "Tethering started: " + interfaces); 286 mInterfaceWasTethered = true; 287 mTetheringStartedLatch.countDown(); 288 } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { 289 Log.d(TAG, "Tethering stopped: " + interfaces); 290 mTetheringStoppedLatch.countDown(); 291 } 292 } 293 awaitInterfaceTethered()294 public void awaitInterfaceTethered() throws Exception { 295 assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", 296 mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 297 } 298 awaitInterfaceUntethered()299 public void awaitInterfaceUntethered() throws Exception { 300 // Don't block teardown if the interface was never tethered. 301 // This is racy because the interface might become tethered right after this check, but 302 // that can only happen in tearDown if startTethering timed out, which likely means 303 // the test has already failed. 304 if (!mInterfaceWasTethered) return; 305 306 assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", 307 mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 308 } 309 310 @Override onError(String ifName, int error)311 public void onError(String ifName, int error) { 312 // Ignore stale callbacks registered by previous test cases. 313 if (mUnregistered) return; 314 315 fail("TetheringEventCallback got error:" + error + " on iface " + ifName); 316 } 317 318 @Override onClientsChanged(Collection<TetheredClient> clients)319 public void onClientsChanged(Collection<TetheredClient> clients) { 320 // Ignore stale callbacks registered by previous test cases. 321 if (mUnregistered) return; 322 323 Log.d(TAG, "Got clients changed: " + clients); 324 mClients = clients; 325 if (clients.size() > 0) { 326 mClientConnectedLatch.countDown(); 327 } 328 } 329 awaitClientConnected()330 public Collection<TetheredClient> awaitClientConnected() throws Exception { 331 assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", 332 mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 333 return mClients; 334 } 335 } 336 enableEthernetTethering(String iface, TetheringRequest request)337 private MyTetheringEventCallback enableEthernetTethering(String iface, 338 TetheringRequest request) throws Exception { 339 MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); 340 mTm.registerTetheringEventCallback(mHandler::post, callback); 341 342 StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { 343 @Override 344 public void onTetheringFailed(int resultCode) { 345 fail("Unexpectedly got onTetheringFailed"); 346 } 347 }; 348 Log.d(TAG, "Starting Ethernet tethering"); 349 mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); 350 callback.awaitInterfaceTethered(); 351 return callback; 352 } 353 enableEthernetTethering(String iface)354 private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { 355 return enableEthernetTethering(iface, 356 new TetheringRequest.Builder(TETHERING_ETHERNET) 357 .setShouldShowEntitlementUi(false).build()); 358 } 359 getMTU(TestNetworkInterface iface)360 private int getMTU(TestNetworkInterface iface) throws SocketException { 361 NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); 362 assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); 363 return nif.getMTU(); 364 } 365 makePacketReader(FileDescriptor fd, int mtu)366 private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { 367 final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); 368 mHandler.post(() -> reader.start()); 369 HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); 370 return reader; 371 } 372 checkVirtualEthernet(TestNetworkInterface iface, int mtu)373 private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { 374 FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); 375 mTapPacketReader = makePacketReader(fd, mtu); 376 mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); 377 checkTetheredClientCallbacks(fd); 378 } 379 runDhcp(FileDescriptor fd, byte[] clientMacAddr)380 private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { 381 // We have to retransmit DHCP requests because IpServer declares itself to be ready before 382 // its DhcpServer is actually started. TODO: fix this race and remove this loop. 383 DhcpPacket offerPacket = null; 384 for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { 385 Log.d(TAG, "Sending DHCP discover"); 386 sendDhcpDiscover(fd, clientMacAddr); 387 offerPacket = getNextDhcpPacket(); 388 if (offerPacket instanceof DhcpOfferPacket) break; 389 } 390 if (!(offerPacket instanceof DhcpOfferPacket)) { 391 throw new TimeoutException("No DHCPOFFER received on interface within timeout"); 392 } 393 394 sendDhcpRequest(fd, offerPacket, clientMacAddr); 395 DhcpPacket ackPacket = getNextDhcpPacket(); 396 if (!(ackPacket instanceof DhcpAckPacket)) { 397 throw new TimeoutException("No DHCPACK received on interface within timeout"); 398 } 399 400 return ackPacket.toDhcpResults(); 401 } 402 checkTetheredClientCallbacks(FileDescriptor fd)403 private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { 404 // Create a fake client. 405 byte[] clientMacAddr = new byte[6]; 406 new Random().nextBytes(clientMacAddr); 407 408 DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); 409 410 final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected(); 411 assertEquals(1, clients.size()); 412 final TetheredClient client = clients.iterator().next(); 413 414 // Check the MAC address. 415 assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); 416 assertEquals(TETHERING_ETHERNET, client.getTetheringType()); 417 418 // Check the hostname. 419 assertEquals(1, client.getAddresses().size()); 420 TetheredClient.AddressInfo info = client.getAddresses().get(0); 421 assertEquals(DHCP_HOSTNAME, info.getHostname()); 422 423 // Check the address is the one that was handed out in the DHCP ACK. 424 assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); 425 426 // Check that the lifetime is correct +/- 10s. 427 final long now = SystemClock.elapsedRealtime(); 428 final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; 429 final String msg = String.format("IP address should have lifetime of %d, got %d", 430 dhcpResults.leaseDuration, actualLeaseDuration); 431 assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); 432 } 433 getNextDhcpPacket()434 private DhcpPacket getNextDhcpPacket() throws ParseException { 435 byte[] packet; 436 while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) { 437 try { 438 return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); 439 } catch (DhcpPacket.ParseException e) { 440 // Not a DHCP packet. Continue. 441 } 442 } 443 return null; 444 } 445 446 private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { 447 private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1); 448 private final Handler mHandler; 449 private final EthernetManager mEm; 450 451 private TetheredInterfaceRequest mRequest; 452 private final CompletableFuture<String> mFuture = new CompletableFuture<>(); 453 TetheredInterfaceRequester(Handler handler, EthernetManager em)454 TetheredInterfaceRequester(Handler handler, EthernetManager em) { 455 mHandler = handler; 456 mEm = em; 457 } 458 459 @Override onAvailable(String iface)460 public void onAvailable(String iface) { 461 Log.d(TAG, "Ethernet interface available: " + iface); 462 mFuture.complete(iface); 463 } 464 465 @Override onUnavailable()466 public void onUnavailable() { 467 mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); 468 } 469 requestInterface()470 public CompletableFuture<String> requestInterface() { 471 assertNull("BUG: more than one tethered interface request", mRequest); 472 Log.d(TAG, "Requesting tethered interface"); 473 mRequest = mEm.requestTetheredInterface(mHandler::post, this); 474 return mFuture; 475 } 476 getInterface()477 public String getInterface() throws Exception { 478 return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 479 } 480 release()481 public void release() { 482 if (mRequest != null) { 483 mFuture.obtrudeException(new IllegalStateException("Request already released")); 484 mRequest.release(); 485 mRequest = null; 486 } 487 } 488 } 489 sendDhcpDiscover(FileDescriptor fd, byte[] macAddress)490 private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception { 491 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, 492 new Random().nextInt() /* transactionId */, (short) 0 /* secs */, 493 macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, 494 false /* rapid commit */, DHCP_HOSTNAME); 495 sendPacket(fd, packet); 496 } 497 sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)498 private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress) 499 throws Exception { 500 DhcpResults results = offerPacket.toDhcpResults(); 501 Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); 502 Inet4Address serverIdentifier = results.serverAddress; 503 ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, 504 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, 505 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, 506 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); 507 sendPacket(fd, packet); 508 } 509 sendPacket(FileDescriptor fd, ByteBuffer packet)510 private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception { 511 assertNotNull("Only tests on virtual interfaces can send packets", fd); 512 Os.write(fd, packet); 513 } 514 assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)515 public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { 516 // Check all fields except the deprecation and expiry times. 517 String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); 518 assertTrue(msg, l1.isSameAddressAs(l2)); 519 assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); 520 assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); 521 } 522 requestWithStaticIpv4(String local, String client)523 private TetheringRequest requestWithStaticIpv4(String local, String client) { 524 LinkAddress localAddr = local == null ? null : new LinkAddress(local); 525 LinkAddress clientAddr = client == null ? null : new LinkAddress(client); 526 return new TetheringRequest.Builder(TETHERING_ETHERNET) 527 .setStaticIpv4Addresses(localAddr, clientAddr) 528 .setShouldShowEntitlementUi(false).build(); 529 } 530 assertInvalidStaticIpv4Request(String iface, String local, String client)531 private void assertInvalidStaticIpv4Request(String iface, String local, String client) 532 throws Exception { 533 try { 534 enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); 535 fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); 536 } catch (IllegalArgumentException | NullPointerException expected) { } 537 } 538 assertInterfaceHasIpAddress(String iface, String expected)539 private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { 540 LinkAddress expectedAddr = new LinkAddress(expected); 541 NetworkInterface nif = NetworkInterface.getByName(iface); 542 for (InterfaceAddress ia : nif.getInterfaceAddresses()) { 543 final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); 544 if (expectedAddr.equals(addr)) { 545 return; 546 } 547 } 548 fail("Expected " + iface + " to have IP address " + expected + ", found " 549 + nif.getInterfaceAddresses()); 550 } 551 createTestInterface()552 private TestNetworkInterface createTestInterface() throws Exception { 553 TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); 554 TestNetworkInterface iface = tnm.createTapInterface(); 555 Log.d(TAG, "Created test interface " + iface.getInterfaceName()); 556 assertNotNull(NetworkInterface.getByName(iface.getInterfaceName())); 557 return iface; 558 } 559 maybeDeleteTestInterface()560 private void maybeDeleteTestInterface() throws Exception { 561 if (mTestIface != null) { 562 mTestIface.getFileDescriptor().close(); 563 Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName()); 564 mTestIface = null; 565 } 566 } 567 } 568