diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 492ce3db3424c422a2c4100b6ddc44ce57487ac6..dbd68ef77cb72b8fe725b7290d9be39afd080fc7 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -18,6 +18,7 @@ package android.net;
 
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -57,6 +58,7 @@ import org.junit.runner.RunWith;
 
 import java.io.FileDescriptor;
 import java.net.Inet4Address;
+import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
@@ -66,6 +68,7 @@ import java.util.Random;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -110,7 +113,7 @@ public class EthernetTetheringTest {
     }
 
     private void cleanUp() throws Exception {
-        mTm.stopTethering(TetheringManager.TETHERING_ETHERNET);
+        mTm.stopTethering(TETHERING_ETHERNET);
         if (mTetheringEventCallback != null) {
             mTetheringEventCallback.awaitInterfaceUntethered();
             mTetheringEventCallback.unregister();
@@ -176,6 +179,49 @@ public class EthernetTetheringTest {
         checkVirtualEthernet(mTestIface, getMTU(mTestIface));
     }
 
+    @Test
+    public void testStaticIpv4() throws Exception {
+        assumeFalse(mEm.isAvailable());
+
+        mEm.setIncludeTestInterfaces(true);
+
+        mTestIface = createTestInterface();
+
+        final String iface = mTetheredInterfaceRequester.getInterface();
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mTestIface.getInterfaceName(), iface);
+
+        assertInvalidStaticIpv4Request(iface, null, null);
+        assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
+        assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
+        assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
+
+        final String localAddr = "192.0.2.3/28";
+        final String clientAddr = "192.0.2.2/28";
+        mTetheringEventCallback = enableEthernetTethering(iface,
+                requestWithStaticIpv4(localAddr, clientAddr));
+
+        mTetheringEventCallback.awaitInterfaceTethered();
+        assertInterfaceHasIpAddress(iface, clientAddr);
+
+        byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
+        byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
+
+        FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
+        mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
+        DhcpResults dhcpResults = runDhcp(fd, client1);
+        assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
+
+        try {
+            runDhcp(fd, client2);
+            fail("Only one client should get an IP address");
+        } catch (TimeoutException expected) { }
+
+    }
+
     @Test
     public void testPhysicalEthernet() throws Exception {
         assumeTrue(mEm.isAvailable());
@@ -271,7 +317,8 @@ public class EthernetTetheringTest {
         }
     }
 
-    private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
+    private MyTetheringEventCallback enableEthernetTethering(String iface,
+            TetheringRequest request) throws Exception {
         MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface);
         mTm.registerTetheringEventCallback(mHandler::post, callback);
 
@@ -282,34 +329,37 @@ public class EthernetTetheringTest {
             }
         };
         Log.d(TAG, "Starting Ethernet tethering");
-        mTm.startTethering(
-                new TetheringRequest.Builder(TetheringManager.TETHERING_ETHERNET).build(),
-                mHandler::post /* executor */,  startTetheringCallback);
+        mTm.startTethering(request, mHandler::post /* executor */,  startTetheringCallback);
         callback.awaitInterfaceTethered();
         return callback;
     }
 
+    private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
+        return enableEthernetTethering(iface,
+                new TetheringRequest.Builder(TETHERING_ETHERNET).build());
+    }
+
     private int getMTU(TestNetworkInterface iface) throws SocketException {
         NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
         assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
         return nif.getMTU();
     }
 
-    private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
-        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
-        mTapPacketReader = new TapPacketReader(mHandler, fd, mtu);
-        mHandler.post(() -> mTapPacketReader.start());
+    private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
+        final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
+        mHandler.post(() -> reader.start());
         HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS);
+        return reader;
+    }
 
+    private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
+        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
+        mTapPacketReader = makePacketReader(fd, mtu);
         mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
         checkTetheredClientCallbacks(fd);
     }
 
-    private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
-        // Create a fake client.
-        byte[] clientMacAddr = new byte[6];
-        new Random().nextBytes(clientMacAddr);
-
+    private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
         // We have to retransmit DHCP requests because IpServer declares itself to be ready before
         // its DhcpServer is actually started. TODO: fix this race and remove this loop.
         DhcpPacket offerPacket = null;
@@ -319,13 +369,25 @@ public class EthernetTetheringTest {
             offerPacket = getNextDhcpPacket();
             if (offerPacket instanceof DhcpOfferPacket) break;
         }
-        assertTrue("No DHCPOFFER received on interface within timeout",
-                offerPacket instanceof DhcpOfferPacket);
+        if (!(offerPacket instanceof DhcpOfferPacket)) {
+            throw new TimeoutException("No DHCPOFFER received on interface within timeout");
+        }
 
         sendDhcpRequest(fd, offerPacket, clientMacAddr);
         DhcpPacket ackPacket = getNextDhcpPacket();
-        assertTrue("No DHCPACK received on interface within timeout",
-                ackPacket instanceof DhcpAckPacket);
+        if (!(ackPacket instanceof DhcpAckPacket)) {
+            throw new TimeoutException("No DHCPACK received on interface within timeout");
+        }
+
+        return ackPacket.toDhcpResults();
+    }
+
+    private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
+        // Create a fake client.
+        byte[] clientMacAddr = new byte[6];
+        new Random().nextBytes(clientMacAddr);
+
+        DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
 
         final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
         assertEquals(1, clients.size());
@@ -333,7 +395,7 @@ public class EthernetTetheringTest {
 
         // Check the MAC address.
         assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
-        assertEquals(TetheringManager.TETHERING_ETHERNET, client.getTetheringType());
+        assertEquals(TETHERING_ETHERNET, client.getTetheringType());
 
         // Check the hostname.
         assertEquals(1, client.getAddresses().size());
@@ -341,7 +403,6 @@ public class EthernetTetheringTest {
         assertEquals(DHCP_HOSTNAME, info.getHostname());
 
         // Check the address is the one that was handed out in the DHCP ACK.
-        DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
 
         // Check that the lifetime is correct +/- 10s.
@@ -441,6 +502,34 @@ public class EthernetTetheringTest {
         assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
     }
 
+    private TetheringRequest requestWithStaticIpv4(String local, String client) {
+        LinkAddress localAddr = local == null ? null : new LinkAddress(local);
+        LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
+        return new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setStaticIpv4Addresses(localAddr, clientAddr).build();
+    }
+
+    private void assertInvalidStaticIpv4Request(String iface, String local, String client)
+            throws Exception {
+        try {
+            enableEthernetTethering(iface, requestWithStaticIpv4(local, client));
+            fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
+        } catch (IllegalArgumentException | NullPointerException expected) { }
+    }
+
+    private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
+        LinkAddress expectedAddr = new LinkAddress(expected);
+        NetworkInterface nif = NetworkInterface.getByName(iface);
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (expectedAddr.equals(addr)) {
+                return;
+            }
+        }
+        fail("Expected " + iface + " to have IP address " + expected + ", found "
+                + nif.getInterfaceAddresses());
+    }
+
     private TestNetworkInterface createTestInterface() throws Exception {
         TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
         TestNetworkInterface iface = tnm.createTapInterface();