Split requestDownstreamAddress into two methods

This CL splits requestDownstreamAddress into two methods:  requestStickyDownstreamAddress and requestDownstreamAddress.

- The requestStickyDownstreamAddress is equivalent to the former requestDownstreamAddress when useLastAddress=true.
- The new requestDownstreamAddress method allows allocating an address without providing the interface type and scope, which is Thread NAT64's use case.


Bug: 369282577


Change-Id: Ib839d121c7b46c492f2c4c1c61b52b5d017fa055
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index f60d7a1..a0604f2 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -725,8 +725,12 @@
 
         if (shouldUseWifiP2pDedicatedIp()) return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
 
-        return mRoutingCoordinator.requestDownstreamAddress(mInterfaceType, scope, useLastAddress,
-                mIpv4PrefixRequest);
+        if (useLastAddress) {
+            return mRoutingCoordinator.requestStickyDownstreamAddress(mInterfaceType, scope,
+                    mIpv4PrefixRequest);
+        }
+
+        return mRoutingCoordinator.requestDownstreamAddress(mIpv4PrefixRequest);
     }
 
     private boolean startIPv6() {
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 8f5e6c4..680e81d 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -258,8 +258,8 @@
                     mIpServer, interfaceParams.index, upstreamPrefixes);
         }
         reset(mNetd, mBpfCoordinator, mCallback, mRoutingCoordinatorManager);
-        when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(),
-                anyBoolean(), any())).thenReturn(mTestAddress);
+        when(mRoutingCoordinatorManager.requestStickyDownstreamAddress(anyInt(), anyInt(),
+                any())).thenReturn(mTestAddress);
     }
 
     @SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run()
@@ -280,8 +280,9 @@
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
-        when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(),
-                anyBoolean(), any())).thenReturn(mTestAddress);
+        when(mRoutingCoordinatorManager.requestStickyDownstreamAddress(anyInt(), anyInt(),
+                any())).thenReturn(mTestAddress);
+        when(mRoutingCoordinatorManager.requestDownstreamAddress(any())).thenReturn(mTestAddress);
         when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD);
         when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */);
 
@@ -348,11 +349,11 @@
         InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
         if (isAtLeastT()) {
             inOrder.verify(mRoutingCoordinatorManager)
-                    .requestDownstreamAddress(
+                    .requestStickyDownstreamAddress(
                             eq(TETHERING_BLUETOOTH),
                             eq(CONNECTIVITY_SCOPE_GLOBAL),
-                            eq(true),
                             any());
+            inOrder.verify(mRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
             inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                     IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
         }
@@ -401,8 +402,9 @@
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
         InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
-        inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(),
-                eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true), any());
+        inOrder.verify(mRoutingCoordinatorManager).requestStickyDownstreamAddress(anyInt(),
+                eq(CONNECTIVITY_SCOPE_GLOBAL), any());
+        inOrder.verify(mRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                 IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -423,8 +425,9 @@
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
         InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
-        inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(),
-                eq(CONNECTIVITY_SCOPE_LOCAL), eq(true), any());
+        inOrder.verify(mRoutingCoordinatorManager).requestStickyDownstreamAddress(anyInt(),
+                eq(CONNECTIVITY_SCOPE_LOCAL), any());
+        inOrder.verify(mRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                   IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -449,7 +452,8 @@
         // When using WiFi P2p dedicated IP, the IpServer just picks the IP address without
         // requesting for it at RoutingCoordinatorManager.
         inOrder.verify(mRoutingCoordinatorManager, never())
-                .requestDownstreamAddress(anyInt(), anyInt(), anyBoolean(), any());
+                .requestStickyDownstreamAddress(anyInt(), anyInt(), any());
+        inOrder.verify(mRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                 IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -731,8 +735,9 @@
         final ArgumentCaptor<LinkProperties> lpCaptor =
                 ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mCallback, mRoutingCoordinatorManager);
-        inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(),
-                eq(CONNECTIVITY_SCOPE_LOCAL), eq(true), any());
+        inOrder.verify(mRoutingCoordinatorManager).requestStickyDownstreamAddress(anyInt(),
+                eq(CONNECTIVITY_SCOPE_LOCAL), any());
+        inOrder.verify(mRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
         inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         // One for ipv4 route, one for ipv6 link local route.
         inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
@@ -745,13 +750,13 @@
         // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
         // onNewPrefixRequest callback.
         final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
-        when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(),
-                anyBoolean(), any())).thenReturn(newAddress);
+        when(mRoutingCoordinatorManager.requestDownstreamAddress(any())).thenReturn(newAddress);
         eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
         mLooper.dispatchAll();
 
-        inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(),
-                eq(CONNECTIVITY_SCOPE_LOCAL), eq(false), any());
+        inOrder.verify(mRoutingCoordinatorManager, never())
+                .requestStickyDownstreamAddress(anyInt(), anyInt(), any());
+        inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(any());
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
         verifyNoMoreInteractions(mCallback);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 1ab5766..08e4ac2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -136,12 +136,16 @@
 
     private LinkAddress requestDownstreamAddress(
             final IpServer ipServer, int scope, boolean useLastAddress) throws Exception {
-        final LinkAddress address =
-                mPrivateAddressCoordinator.requestDownstreamAddress(
-                        ipServer.interfaceType(),
-                        scope,
-                        useLastAddress,
-                        ipServer.getIpv4PrefixRequest());
+        LinkAddress address;
+        if (useLastAddress) {
+            address =
+                    mPrivateAddressCoordinator.requestStickyDownstreamAddress(
+                            ipServer.interfaceType(), scope, ipServer.getIpv4PrefixRequest());
+        } else {
+            address =
+                    mPrivateAddressCoordinator.requestDownstreamAddress(
+                            ipServer.getIpv4PrefixRequest());
+        }
         when(ipServer.getAddress()).thenReturn(address);
         return address;
     }
diff --git a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
index 097824f..7688e6a 100644
--- a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
+++ b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
@@ -115,20 +115,27 @@
     void maybeRemoveDeprecatedUpstreams();
 
    /**
-    * Request an IPv4 address for the downstream.
+    * Request an IPv4 address for the downstream. Return the last time used address for the
+    * provided (interfaceType, scope) pair if possible.
     *
     * @param interfaceType the Tethering type (see TetheringManager#TETHERING_*).
     * @param scope CONNECTIVITY_SCOPE_GLOBAL or CONNECTIVITY_SCOPE_LOCAL
-    * @param useLastAddress whether to use the last address
     * @param request a {@link IIpv4PrefixRequest} to report conflicts
     * @return an IPv4 address allocated for the downstream, could be null
     */
     @nullable
-    LinkAddress requestDownstreamAddress(
+    LinkAddress requestStickyDownstreamAddress(
             in int interfaceType,
             in int scope,
-            in boolean useLastAddress,
             in IIpv4PrefixRequest request);
+   /**
+    * Request an IPv4 address for the downstream.
+    *
+    * @param request a {@link IIpv4PrefixRequest} to report conflicts
+    * @return an IPv4 address allocated for the downstream, could be null
+    */
+    @nullable
+    LinkAddress requestDownstreamAddress(in IIpv4PrefixRequest request);
 
     /** Release the IPv4 address allocated for the downstream. */
     void releaseDownstream(in IIpv4PrefixRequest request);
diff --git a/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java b/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java
index 990358d..7fcbd4e 100644
--- a/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java
+++ b/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java
@@ -78,7 +78,7 @@
     private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
     // The downstreams are indexed by Ipv4PrefixRequest, which is a wrapper of the Binder object of
     // IIpv4PrefixRequest.
-    private final ArrayMap<Ipv4PrefixRequest, Downstream> mDownstreams;
+    private final ArrayMap<Ipv4PrefixRequest, LinkAddress> mDownstreams;
     private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
     private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
     private final List<IpPrefix> mTetheringPrefixes;
@@ -168,10 +168,10 @@
     }
 
     private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
-        for (Map.Entry<Ipv4PrefixRequest, Downstream> entry : mDownstreams.entrySet()) {
+        for (Map.Entry<Ipv4PrefixRequest, LinkAddress> entry : mDownstreams.entrySet()) {
             final Ipv4PrefixRequest request = entry.getKey();
-            final Downstream downstream = entry.getValue();
-            final IpPrefix target = asIpPrefix(downstream.getAddress());
+            final LinkAddress downstream = entry.getValue();
+            final IpPrefix target = asIpPrefix(downstream);
 
             for (IpPrefix source : prefixes) {
                 if (isConflictPrefix(source, target)) {
@@ -209,12 +209,14 @@
     // TetheringRequest has been set a static IPv4 address.
 
     /**
-     * Pick a random available address and mark its prefix as in use for the provided IpServer,
-     * returns null if there is no available address.
+     * Request a downstream address for the provided IIpv4PrefixRequest.
+     *
+     * This method will first try to return the last time used address for the provided
+     * (interfaceType, scope) pair if possible. If not, it will pick a random available address and
+     * mark its prefix as in use for the provided IIpv4PrefixRequest.
      */
     @Nullable
-    public LinkAddress requestDownstreamAddress(int interfaceType, final int scope,
-            boolean useLastAddress,
+    public LinkAddress requestStickyDownstreamAddress(int interfaceType, final int scope,
             IIpv4PrefixRequest request) {
         final Ipv4PrefixRequest wrappedRequest = new Ipv4PrefixRequest(request);
         final AddressKey addrKey = new AddressKey(interfaceType, scope);
@@ -222,20 +224,32 @@
         // Once tethering could support multiple interface with the same type,
         // TetheringSoftApCallback would need to handle it among others.
         final LinkAddress cachedAddress = mCachedAddresses.get(addrKey);
-        if (useLastAddress && cachedAddress != null
-                && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
-            mDownstreams.put(wrappedRequest, new Downstream(interfaceType, cachedAddress));
+        if (cachedAddress != null && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
+            mDownstreams.put(wrappedRequest, cachedAddress);
             return cachedAddress;
         }
 
+        final LinkAddress newAddress = requestDownstreamAddress(request);
+        if (newAddress != null) {
+            mCachedAddresses.put(addrKey, newAddress);
+        }
+        return newAddress;
+    }
+
+    /**
+     * Pick a random available address and mark its prefix as in use for the provided
+     * IIpv4PrefixRequest. Return null if there is no available address.
+     */
+    @Nullable
+    public LinkAddress requestDownstreamAddress(IIpv4PrefixRequest request) {
+        final Ipv4PrefixRequest wrappedRequest = new Ipv4PrefixRequest(request);
         final int prefixIndex = getRandomPrefixIndex();
         for (int i = 0; i < mTetheringPrefixes.size(); i++) {
             final IpPrefix prefixRange = mTetheringPrefixes.get(
                     (prefixIndex + i) % mTetheringPrefixes.size());
             final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
             if (newAddress != null) {
-                mDownstreams.put(wrappedRequest, new Downstream(interfaceType, newAddress));
-                mCachedAddresses.put(addrKey, newAddress);
+                mDownstreams.put(wrappedRequest, newAddress);
                 return newAddress;
             }
         }
@@ -379,8 +393,8 @@
 
         // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
         // in mCachedAddresses.
-        for (Downstream downstream : mDownstreams.values()) {
-            final IpPrefix target = asIpPrefix(downstream.getAddress());
+        for (LinkAddress downstream : mDownstreams.values()) {
+            final IpPrefix target = asIpPrefix(downstream);
 
             if (isConflictPrefix(prefix, target)) return target;
         }
@@ -417,24 +431,6 @@
         }
     }
 
-    private static final class Downstream {
-        private final int mInterfaceType;
-        private final LinkAddress mAddress;
-
-        private Downstream(int interfaceType, LinkAddress address) {
-            mInterfaceType = interfaceType;
-            mAddress = address;
-        }
-
-        public int getInterfaceType() {
-            return mInterfaceType;
-        }
-
-        public LinkAddress getAddress() {
-            return mAddress;
-        }
-    }
-
     private static class AddressKey {
         private final int mTetheringType;
         private final int mScope;
@@ -481,8 +477,8 @@
 
         pw.println("mDownstreams:");
         pw.increaseIndent();
-        for (Downstream downstream : mDownstreams.values()) {
-            pw.println(downstream.getInterfaceType() + " - " + downstream.getAddress());
+        for (LinkAddress downstream : mDownstreams.values()) {
+            pw.println(downstream);
         }
         pw.decreaseIndent();
 
diff --git a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
index 9ea0947..f5af30c 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
@@ -195,23 +195,35 @@
     }
 
     /**
-     * Request an IPv4 address for the downstream.
+     * Request an IPv4 address for the downstream. Return the last time used address for the
+     * provided (interfaceType, scope) pair if possible.
      *
      * @param interfaceType the Tethering type (see TetheringManager#TETHERING_*).
      * @param scope CONNECTIVITY_SCOPE_GLOBAL or CONNECTIVITY_SCOPE_LOCAL
-     * @param useLastAddress whether to use the last address
      * @param request a {@link IIpv4PrefixRequest} to report conflicts
      * @return an IPv4 address allocated for the downstream, could be null
      */
     @Nullable
-    public LinkAddress requestDownstreamAddress(
+    public LinkAddress requestStickyDownstreamAddress(
             int interfaceType,
             int scope,
-            boolean useLastAddress,
             IIpv4PrefixRequest request) {
         try {
-            return mService.requestDownstreamAddress(
-                    interfaceType, scope, useLastAddress, request);
+            return mService.requestStickyDownstreamAddress(interfaceType, scope, request);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request an IPv4 address for the downstream.
+     *
+     * @param request a {@link IIpv4PrefixRequest} to report conflicts
+     * @return an IPv4 address allocated for the downstream, could be null
+     */
+    public LinkAddress requestDownstreamAddress(IIpv4PrefixRequest request) {
+        try {
+            return mService.requestDownstreamAddress(request);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
index d16c234..51eb47c 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
@@ -284,23 +284,40 @@
     }
 
     /**
-     * Request an IPv4 address for the downstream.
+     * Request an IPv4 address for the downstream. Return the last time used address for the
+     * provided (interfaceType, scope) pair if possible.
      *
      * @param interfaceType the Tethering type (see TetheringManager#TETHERING_*).
      * @param scope CONNECTIVITY_SCOPE_GLOBAL or CONNECTIVITY_SCOPE_LOCAL
-     * @param useLastAddress whether to use the last address
      * @param request a {@link IIpv4PrefixRequest} to report conflicts
      * @return an IPv4 address allocated for the downstream, could be null
      */
     @Override
-    public LinkAddress requestDownstreamAddress(int interfaceType, int scope,
-            boolean useLastAddress, IIpv4PrefixRequest request) {
+    public LinkAddress requestStickyDownstreamAddress(int interfaceType, int scope,
+            IIpv4PrefixRequest request) {
         Objects.requireNonNull(request);
         return BinderUtils.withCleanCallingIdentity(
                 () -> {
                     synchronized (mPrivateAddressCoordinatorLock) {
-                        return mPrivateAddressCoordinator.requestDownstreamAddress(
-                                interfaceType, scope, useLastAddress, request);
+                        return mPrivateAddressCoordinator.requestStickyDownstreamAddress(
+                                interfaceType, scope, request);
+                    }
+                });
+    }
+
+    /**
+     * Request an IPv4 address for the downstream.
+     *
+     * @param request a {@link IIpv4PrefixRequest} to report conflicts
+     * @return an IPv4 address allocated for the downstream, could be null
+     */
+    @Override
+    public LinkAddress requestDownstreamAddress(IIpv4PrefixRequest request) {
+        Objects.requireNonNull(request);
+        return BinderUtils.withCleanCallingIdentity(
+                () -> {
+                    synchronized (mPrivateAddressCoordinatorLock) {
+                        return mPrivateAddressCoordinator.requestDownstreamAddress(request);
                     }
                 });
     }