Introduce PrivateAddressCoordinator methods at RoutingCoordinatorManager
This CL exposes PrivateAddressCoordinator methods at
RoutingCoordinatorManager and implements the methods in
RoutingCoordinatorService.
The objective is to allow a system service like Thread to request an
IPv4 address allocation.
Bug: 350699020
Change-Id: I65dc309f0b503d544e0fddc1168131fa6174796c
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 50f82cf..db97986 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -24,7 +24,6 @@
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
-import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static java.util.Arrays.asList;
@@ -34,16 +33,13 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.ip.IpServer;
+import android.os.RemoteException;
import android.util.ArrayMap;
-import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.net.module.util.DeviceConfigUtils;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -51,6 +47,8 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
@@ -62,7 +60,7 @@
* coordinator is responsible for recording all of network assigned addresses and dispatched
* free address to downstream interfaces.
*
- * This class is not thread-safe and should be accessed on the same tethering internal thread.
+ * This class is not thread-safe.
* @hide
*/
public class PrivateAddressCoordinator {
@@ -78,7 +76,9 @@
// when tethering is down. Instead tethering would remove all deprecated upstreams from
// mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
- private final ArraySet<IpServer> mDownstreams;
+ // The downstreams are indexed by Ipv4PrefixRequest, which is a wrapper of the Binder object of
+ // IIpv4PrefixRequest.
+ private final ArrayMap<Ipv4PrefixRequest, Downstream> 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;
@@ -116,7 +116,7 @@
@VisibleForTesting
public PrivateAddressCoordinator(Supplier<Network[]> getAllNetworksSupplier,
Dependencies deps) {
- mDownstreams = new ArraySet<>();
+ mDownstreams = new ArrayMap<>();
mUpstreamPrefixMap = new ArrayMap<>();
mGetAllNetworksSupplier = getAllNetworksSupplier;
mDeps = deps;
@@ -168,12 +168,18 @@
}
private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
+ for (Map.Entry<Ipv4PrefixRequest, Downstream> entry : mDownstreams.entrySet()) {
+ final Ipv4PrefixRequest request = entry.getKey();
+ final Downstream downstream = entry.getValue();
+ final IpPrefix target = asIpPrefix(downstream.getAddress());
for (IpPrefix source : prefixes) {
if (isConflictPrefix(source, target)) {
- downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ try {
+ request.getRequest().onIpv4PrefixConflict(target);
+ } catch (RemoteException ignored) {
+ // ignore
+ }
break;
}
}
@@ -199,21 +205,26 @@
mUpstreamPrefixMap.removeAll(toBeRemoved);
}
+ // TODO: There needs to be a reserveDownstreamAddress() method for the cases where
+ // 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.
*/
@Nullable
- public LinkAddress requestDownstreamAddress(final IpServer ipServer, final int scope,
- boolean useLastAddress) {
- final AddressKey addrKey = new AddressKey(ipServer.interfaceType(), scope);
+ public LinkAddress requestDownstreamAddress(int interfaceType, final int scope,
+ boolean useLastAddress,
+ IIpv4PrefixRequest request) {
+ final Ipv4PrefixRequest wrappedRequest = new Ipv4PrefixRequest(request);
+ final AddressKey addrKey = new AddressKey(interfaceType, scope);
// This ensures that tethering isn't started on 2 different interfaces with the same type.
// 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.add(ipServer);
+ mDownstreams.put(wrappedRequest, new Downstream(interfaceType, cachedAddress));
return cachedAddress;
}
@@ -223,7 +234,7 @@
(prefixIndex + i) % mTetheringPrefixes.size());
final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
if (newAddress != null) {
- mDownstreams.add(ipServer);
+ mDownstreams.put(wrappedRequest, new Downstream(interfaceType, newAddress));
mCachedAddresses.put(addrKey, newAddress);
return newAddress;
}
@@ -327,8 +338,8 @@
}
/** Release downstream record for IpServer. */
- public void releaseDownstream(final IpServer ipServer) {
- mDownstreams.remove(ipServer);
+ public void releaseDownstream(IIpv4PrefixRequest request) {
+ mDownstreams.remove(new Ipv4PrefixRequest(request));
}
/** Clear current upstream prefixes records. */
@@ -368,8 +379,8 @@
// IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
// in mCachedAddresses.
- for (IpServer downstream : mDownstreams) {
- final IpPrefix target = getDownstreamPrefix(downstream);
+ for (Downstream downstream : mDownstreams.values()) {
+ final IpPrefix target = asIpPrefix(downstream.getAddress());
if (isConflictPrefix(prefix, target)) return target;
}
@@ -377,11 +388,51 @@
return null;
}
- @NonNull
- private IpPrefix getDownstreamPrefix(final IpServer downstream) {
- final LinkAddress address = downstream.getAddress();
+ private static IpPrefix asIpPrefix(LinkAddress addr) {
+ return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
+ }
- return asIpPrefix(address);
+ private static final class Ipv4PrefixRequest {
+ private final IIpv4PrefixRequest mRequest;
+
+ Ipv4PrefixRequest(IIpv4PrefixRequest request) {
+ mRequest = request;
+ }
+
+ public IIpv4PrefixRequest getRequest() {
+ return mRequest;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof Ipv4PrefixRequest)) return false;
+ return Objects.equals(
+ mRequest.asBinder(), ((Ipv4PrefixRequest) obj).mRequest.asBinder());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mRequest.asBinder());
+ }
+ }
+
+ 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 {
@@ -412,6 +463,7 @@
}
}
+ // TODO: dump PrivateAddressCoordinator when dumping RoutingCoordinatorService.
void dump(final IndentingPrintWriter pw) {
pw.println("mTetheringPrefixes:");
pw.increaseIndent();
@@ -429,8 +481,8 @@
pw.println("mDownstreams:");
pw.increaseIndent();
- for (IpServer ipServer : mDownstreams) {
- pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
+ for (Downstream downstream : mDownstreams.values()) {
+ pw.println(downstream.getInterfaceType() + " - " + downstream.getAddress());
}
pw.decreaseIndent();
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index cc878d5..34f2897 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -136,7 +136,10 @@
public RoutingCoordinatorManager getRoutingCoordinator(Context context, SharedLog log) {
IBinder binder;
if (!SdkLevel.isAtLeastS()) {
- binder = new RoutingCoordinatorService(getINetd(context, log));
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+ binder =
+ new RoutingCoordinatorService(
+ getINetd(context, log), cm::getAllNetworks, context);
} else {
binder = ConnectivityInternalApiUtil.getRoutingCoordinator(context);
}
@@ -175,14 +178,6 @@
}
/**
- * Make PrivateAddressCoordinator to be used by Tethering.
- */
- public PrivateAddressCoordinator makePrivateAddressCoordinator(Context ctx) {
- final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
- return new PrivateAddressCoordinator(cm::getAllNetworks, ctx);
- }
-
- /**
* Make BluetoothPanShim object to enable/disable bluetooth tethering.
*
* TODO: use BluetoothPan directly when mainline module is built with API 32.
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index cb62ae1..a04ebdd 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2028,7 +2028,8 @@
mCdmps = null;
}
- mRoutingCoordinatorService = new RoutingCoordinatorService(netd);
+ mRoutingCoordinatorService =
+ new RoutingCoordinatorService(netd, this::getAllNetworks, mContext);
mMulticastRoutingCoordinatorService =
mDeps.makeMulticastRoutingCoordinatorService(mHandler);
diff --git a/staticlibs/device/com/android/net/module/util/IIpv4PrefixRequest.aidl b/staticlibs/device/com/android/net/module/util/IIpv4PrefixRequest.aidl
new file mode 100644
index 0000000..cc1c19c
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/IIpv4PrefixRequest.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+
+/** @hide */
+// TODO: b/350630377 - This @Descriptor annotation workaround is to prevent the class from being
+// jarjared which changes the DESCRIPTOR and casues "java.lang.SecurityException: Binder invocation
+// to an incorrect interface" when calling the IPC.
+@Descriptor("value=no.jarjar.com.android.net.module.util.IIpv4PrefixRequest")
+interface IIpv4PrefixRequest {
+ void onIpv4PrefixConflict(in IpPrefix ipPrefix);
+}
diff --git a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
index 72a4a94..097824f 100644
--- a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
+++ b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
@@ -16,8 +16,14 @@
package com.android.net.module.util;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.RouteInfo;
+import com.android.net.module.util.IIpv4PrefixRequest;
+
/** @hide */
// TODO: b/350630377 - This @Descriptor annotation workaround is to prevent the DESCRIPTOR from
// being jarjared which changes the DESCRIPTOR and casues "java.lang.SecurityException: Binder
@@ -96,4 +102,34 @@
* cause of the failure.
*/
void removeInterfaceForward(in String fromIface, in String toIface);
+
+ /** Update the prefix of an upstream. */
+ void updateUpstreamPrefix(in @nullable LinkProperties lp,
+ in @nullable NetworkCapabilities nc,
+ in Network network);
+
+ /** Remove the upstream prefix of the given {@link Network}. */
+ void removeUpstreamPrefix(in Network network);
+
+ /** Remove the deprecated upstream networks if any. */
+ void maybeRemoveDeprecatedUpstreams();
+
+ /**
+ * Request an IPv4 address for the downstream.
+ *
+ * @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(
+ in int interfaceType,
+ in int scope,
+ in boolean useLastAddress,
+ 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/RoutingCoordinatorManager.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
index 02e3643..9ea0947 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
@@ -17,17 +17,27 @@
package com.android.net.module.util;
import android.content.Context;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.RouteInfo;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* A manager class for talking to the routing coordinator service.
*
* This class should only be used by the connectivity and tethering module. This is enforced
* by the build rules. Do not change build rules to gain access to this class from elsewhere.
+ *
+ * This class has following functionalities:
+ * - Manage routes and forwarding for networks.
+ * - Manage IPv4 prefix allocation for network interfaces.
+ *
* @hide
*/
public class RoutingCoordinatorManager {
@@ -154,4 +164,65 @@
throw e.rethrowFromSystemServer();
}
}
+
+ // PrivateAddressCoordinator methods:
+
+ /** Update the prefix of an upstream. */
+ public void updateUpstreamPrefix(LinkProperties lp, NetworkCapabilities nc, Network network) {
+ try {
+ mService.updateUpstreamPrefix(lp, nc, network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Remove the upstream prefix of the given {@link Network}. */
+ public void removeUpstreamPrefix(Network network) {
+ try {
+ mService.removeUpstreamPrefix(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Remove the deprecated upstream networks if any. */
+ public void maybeRemoveDeprecatedUpstreams() {
+ try {
+ mService.maybeRemoveDeprecatedUpstreams();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request an IPv4 address for the downstream.
+ *
+ * @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(
+ int interfaceType,
+ int scope,
+ boolean useLastAddress,
+ IIpv4PrefixRequest request) {
+ try {
+ return mService.requestDownstreamAddress(
+ interfaceType, scope, useLastAddress, request);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Release the IPv4 address allocated for the downstream. */
+ public void releaseDownstream(IIpv4PrefixRequest request) {
+ try {
+ mService.releaseDownstream(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 c75b860..d16c234 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
@@ -19,8 +19,13 @@
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
import android.annotation.NonNull;
+import android.content.Context;
import android.net.INetd;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.RouteInfo;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -28,8 +33,10 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
+import java.util.function.Supplier;
/**
* Class to coordinate routing across multiple clients.
@@ -45,8 +52,22 @@
private static final String TAG = RoutingCoordinatorService.class.getSimpleName();
private final INetd mNetd;
- public RoutingCoordinatorService(@NonNull INetd netd) {
+ private final Object mPrivateAddressCoordinatorLock = new Object();
+ @GuardedBy("mPrivateAddressCoordinatorLock")
+ private final PrivateAddressCoordinator mPrivateAddressCoordinator;
+
+ public RoutingCoordinatorService(@NonNull INetd netd,
+ @NonNull Supplier<Network[]> getAllNetworksSupplier,
+ @NonNull Context context) {
+ this(netd, getAllNetworksSupplier, new PrivateAddressCoordinator.Dependencies(context));
+ }
+
+ @VisibleForTesting
+ public RoutingCoordinatorService(@NonNull INetd netd,
+ @NonNull Supplier<Network[]> getAllNetworksSupplier,
+ @NonNull PrivateAddressCoordinator.Dependencies pacDeps) {
mNetd = netd;
+ mPrivateAddressCoordinator = new PrivateAddressCoordinator(getAllNetworksSupplier, pacDeps);
}
/**
@@ -225,4 +246,74 @@
}
}
}
+
+ // PrivateAddressCoordinator methods:
+
+ /** Update the prefix of an upstream. */
+ @Override
+ public void updateUpstreamPrefix(LinkProperties lp, NetworkCapabilities nc, Network network) {
+ BinderUtils.withCleanCallingIdentity(
+ () -> {
+ synchronized (mPrivateAddressCoordinatorLock) {
+ mPrivateAddressCoordinator.updateUpstreamPrefix(lp, nc, network);
+ }
+ });
+ }
+
+ /** Remove the upstream prefix of the given {@link Network}. */
+ @Override
+ public void removeUpstreamPrefix(Network network) {
+ Objects.requireNonNull(network);
+ BinderUtils.withCleanCallingIdentity(
+ () -> {
+ synchronized (mPrivateAddressCoordinatorLock) {
+ mPrivateAddressCoordinator.removeUpstreamPrefix(network);
+ }
+ });
+ }
+
+ /** Remove the deprecated upstream networks if any. */
+ @Override
+ public void maybeRemoveDeprecatedUpstreams() {
+ BinderUtils.withCleanCallingIdentity(
+ () -> {
+ synchronized (mPrivateAddressCoordinatorLock) {
+ mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams();
+ }
+ });
+ }
+
+ /**
+ * Request an IPv4 address for the downstream.
+ *
+ * @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) {
+ Objects.requireNonNull(request);
+ return BinderUtils.withCleanCallingIdentity(
+ () -> {
+ synchronized (mPrivateAddressCoordinatorLock) {
+ return mPrivateAddressCoordinator.requestDownstreamAddress(
+ interfaceType, scope, useLastAddress, request);
+ }
+ });
+ }
+
+ /** Release the IPv4 address allocated for the downstream. */
+ @Override
+ public void releaseDownstream(IIpv4PrefixRequest request) {
+ Objects.requireNonNull(request);
+ BinderUtils.withCleanCallingIdentity(
+ () -> {
+ synchronized (mPrivateAddressCoordinatorLock) {
+ mPrivateAddressCoordinator.releaseDownstream(request);
+ }
+ });
+ }
}