Assign specific client address to dhcp server

Bug: 141256482
Test: manual
      atest TetheringTests

Merged-In: Ief76c98c843ba5420224cbf0f34464f366c891b7
Change-Id: Ief76c98c843ba5420224cbf0f34464f366c891b7
diff --git a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index d6bc063..82a26be 100644
--- a/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -18,10 +18,12 @@
 
 import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
 
-import android.annotation.NonNull;
 import android.net.LinkAddress;
 import android.util.ArraySet;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.net.Inet4Address;
 import java.util.Collection;
 import java.util.Collections;
@@ -160,6 +162,17 @@
         return this;
     }
 
+    /**
+     * Set the client address to tell DHCP server only offer this address.
+     * The client's prefix length is the same as server's.
+     *
+     * <p>If not set, the default value is null.
+     */
+    public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) {
+        this.clientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr);
+        return this;
+    }
+
     private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
         int[] res = new int[addrs.size()];
         int i = 0;
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 5b6fe91..1dac5b7 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -18,6 +18,7 @@
 
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 import static android.net.util.NetworkConstants.FF;
@@ -511,17 +512,24 @@
         }
     }
 
-    private boolean startDhcp(Inet4Address addr, int prefixLen) {
+    private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
         if (mUsingLegacyDhcp) {
             return true;
         }
+
+        final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
+        final int prefixLen = serverLinkAddr.getPrefixLength();
+        final Inet4Address clientAddr = clientLinkAddr == null ? null :
+                (Inet4Address) clientLinkAddr.getAddress();
+
         final DhcpServingParamsParcel params;
         params = new DhcpServingParamsParcelExt()
                 .setDefaultRouters(addr)
                 .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
                 .setDnsServers(addr)
-                .setServerAddr(new LinkAddress(addr, prefixLen))
-                .setMetered(true);
+                .setServerAddr(serverLinkAddr)
+                .setMetered(true)
+                .setSingleClientAddr(clientAddr);
         // TODO: also advertise link MTU
 
         mDhcpServerStartIndex++;
@@ -556,9 +564,10 @@
         }
     }
 
-    private boolean configureDhcp(boolean enable, Inet4Address addr, int prefixLen) {
+    private boolean configureDhcp(boolean enable, final LinkAddress serverAddr,
+            final LinkAddress clientAddr) {
         if (enable) {
-            return startDhcp(addr, prefixLen);
+            return startDhcp(serverAddr, clientAddr);
         } else {
             stopDhcp();
             return true;
@@ -606,7 +615,7 @@
                 // code that calls into NetworkManagementService directly.
                 srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
                 mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
-                return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
+                return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
             }
             mIpv4Address = new LinkAddress(srvAddr, prefixLen);
         } catch (IllegalArgumentException e) {
@@ -643,7 +652,7 @@
             mLinkProperties.removeRoute(route);
         }
 
-        return configureDhcp(enabled, srvAddr, prefixLen);
+        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
     }
 
     private String getRandomWifiIPv4Address() {
@@ -962,7 +971,14 @@
     }
 
     private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
-        if (request == null) return;
+        // Ignore static address configuration if they are invalid or null. In theory, static
+        // addresses should not be invalid here because TetheringManager do not allow caller to
+        // specify invalid static address configuration.
+        if (request == null || request.localIPv4Address == null
+                || request.staticClientAddress == null || !checkStaticAddressConfiguration(
+                request.localIPv4Address, request.staticClientAddress)) {
+            return;
+        }
 
         mStaticIpv4ServerAddr = request.localIPv4Address;
         mStaticIpv4ClientAddr = request.staticClientAddress;