Add a routing coordinator class

Test: In followup changes : RoutingCoordinatorManagerTest
Change-Id: Ia6811d614e02123a072c7638291828745abae051
This commit is contained in:
Chalard Jean
2023-08-25 12:50:37 +09:00
parent 6c83f00b09
commit 2fb66f1b28
17 changed files with 554 additions and 68 deletions

View File

@@ -44,6 +44,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.RoutingCoordinatorManager;
import android.net.TetheredClient;
import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
@@ -71,6 +72,7 @@ import com.android.internal.util.StateMachine;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.InterfaceController;
import com.android.net.module.util.ip.IpNeighborMonitor;
@@ -245,6 +247,11 @@ public class IpServer extends StateMachine {
private final INetd mNetd;
@NonNull
private final BpfCoordinator mBpfCoordinator;
// Contains null if the connectivity module is unsupported, as the routing coordinator is not
// available. Must use LateSdk because MessageUtils enumerates fields in this class, so it
// must be able to find all classes at runtime.
@NonNull
private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinator;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
@@ -307,13 +314,15 @@ public class IpServer extends StateMachine {
// object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
INetd netd, @NonNull BpfCoordinator bpfCoordinator,
@Nullable LateSdk<RoutingCoordinatorManager> routingCoordinator, Callback callback,
TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
TetheringMetrics tetheringMetrics, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
mBpfCoordinator = coordinator;
mBpfCoordinator = bpfCoordinator;
mRoutingCoordinator = routingCoordinator;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -807,23 +816,33 @@ public class IpServer extends StateMachine {
for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
}
private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
private void addInterfaceToNetwork(final int netId, @NonNull final String ifaceName) {
try {
// It's safe to call networkAddInterface() even if
// the interface is already in the local_network.
mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
try {
// Add routes from local network. Note that adding routes that
// already exist does not cause an error (EEXIST is silently ignored).
NetdUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
} catch (IllegalStateException e) {
mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
return;
if (null != mRoutingCoordinator.value) {
// TODO : remove this call in favor of using the LocalNetworkConfiguration
// correctly, which will let ConnectivityService do it automatically.
mRoutingCoordinator.value.addInterfaceToNetwork(netId, ifaceName);
} else {
mNetd.networkAddInterface(netId, ifaceName);
}
} catch (ServiceSpecificException | RemoteException e) {
mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
return;
}
}
private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
// It's safe to call addInterfaceToNetwork() even if
// the interface is already in the local_network.
addInterfaceToNetwork(INetd.LOCAL_NET_ID, mIfaceName);
try {
// Add routes from local network. Note that adding routes that
// already exist does not cause an error (EEXIST is silently ignored).
NetdUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
} catch (IllegalStateException e) {
mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
return;
}
for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
}

View File

@@ -90,6 +90,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.RoutingCoordinatorManager;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
import android.net.TetheringCallbackStartedParcel;
@@ -136,6 +137,7 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
@@ -250,6 +252,10 @@ public class Tethering {
private final Handler mHandler;
private final INetd mNetd;
private final NetdCallback mNetdCallback;
// Contains null if the connectivity module is unsupported, as the routing coordinator is not
// available. Must use LateSdk because MessageUtils enumerates fields in this class, so it
// must be able to find all classes at runtime.
@NonNull private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinator;
private final UserRestrictionActionListener mTetheringRestriction;
private final ActiveDataSubIdListener mActiveDataSubIdListener;
private final ConnectedClientsTracker mConnectedClientsTracker;
@@ -296,6 +302,7 @@ public class Tethering {
mDeps = deps;
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mRoutingCoordinator = mDeps.getRoutingCoordinator(mContext);
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
mTetheringMetrics = mDeps.getTetheringMetrics();
@@ -2835,8 +2842,9 @@ public class Tethering {
mLog.i("adding IpServer for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig, mPrivateAddressCoordinator,
mTetheringMetrics, mDeps.getIpServerDependencies()), isNcm);
mRoutingCoordinator, makeControlCallback(), mConfig,
mPrivateAddressCoordinator, mTetheringMetrics,
mDeps.getIpServerDependencies()), isNcm);
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}

View File

@@ -16,11 +16,14 @@
package com.android.networkstack.tethering;
import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.content.Context;
import android.net.INetd;
import android.net.RoutingCoordinatorManager;
import android.net.connectivity.TiramisuConnectivityInternalApiUtil;
import android.net.ip.IpServer;
import android.os.Build;
import android.os.Handler;
@@ -33,6 +36,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.android.internal.util.StateMachine;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.BluetoothPanShimImpl;
import com.android.networkstack.apishim.common.BluetoothPanShim;
@@ -121,6 +126,16 @@ public abstract class TetheringDependencies {
(IBinder) context.getSystemService(Context.NETD_SERVICE));
}
/**
* Get the routing coordinator, or null if below S.
*/
@Nullable
public LateSdk<RoutingCoordinatorManager> getRoutingCoordinator(Context context) {
if (!SdkLevel.isAtLeastS()) return new LateSdk<>(null);
return new LateSdk<>(
TiramisuConnectivityInternalApiUtil.getRoutingCoordinatorManager(context));
}
/**
* Get a reference to the TetheringNotificationUpdater to be used by tethering.
*/
@@ -135,7 +150,7 @@ public abstract class TetheringDependencies {
public abstract Looper getTetheringLooper();
/**
* Get Context of TetheringSerice.
* Get Context of TetheringService.
*/
public abstract Context getContext();

View File

@@ -62,6 +62,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -80,6 +81,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.RoutingCoordinatorManager;
import android.net.TetherOffloadRuleParcel;
import android.net.TetherStatsParcel;
import android.net.dhcp.DhcpServerCallbacks;
@@ -99,9 +101,11 @@ import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.bpf.Tether4Key;
@@ -193,6 +197,8 @@ public class IpServerTest {
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@Mock private PrivateAddressCoordinator mAddressCoordinator;
private final LateSdk<RoutingCoordinatorManager> mRoutingCoordinatorManager =
new LateSdk<>(SdkLevel.isAtLeastS() ? mock(RoutingCoordinatorManager.class) : null);
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
@@ -249,7 +255,8 @@ public class IpServerTest {
mBpfCoordinator = spy(new BpfCoordinator(mBpfDeps));
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
mCallback, mTetherConfig, mAddressCoordinator, mTetheringMetrics, mDependencies);
mRoutingCoordinatorManager, mCallback, mTetherConfig, mAddressCoordinator,
mTetheringMetrics, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -396,8 +403,8 @@ public class IpServerTest {
when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mBpfCoordinator, mCallback, mTetherConfig, mAddressCoordinator,
mTetheringMetrics, mDependencies);
mNetd, mBpfCoordinator, mRoutingCoordinatorManager, mCallback, mTetherConfig,
mAddressCoordinator, mTetheringMetrics, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(

View File

@@ -142,6 +142,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.RoutingCoordinatorManager;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
import android.net.TetheredClient.AddressInfo;
@@ -191,6 +192,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.SdkUtil.LateSdk;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.IpNeighborMonitor;
import com.android.networkstack.apishim.common.BluetoothPanShim;
@@ -482,6 +484,12 @@ public class TetheringTest {
return mEntitleMgr;
}
@Nullable
@Override
public LateSdk<RoutingCoordinatorManager> getRoutingCoordinator(final Context context) {
return new LateSdk<>(null);
}
@Override
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
int subId) {

View File

@@ -292,17 +292,20 @@ java_genrule {
// Library providing limited APIs within the connectivity module, so that R+ components like
// Tethering have a controlled way to depend on newer components like framework-connectivity that
// are not loaded on R.
// Note that this target needs to have access to hidden classes, and as such needs to list
// the full libraries instead of the .impl lib (which only expose API classes).
java_library {
name: "connectivity-internal-api-util",
sdk_version: "module_current",
libs: [
"androidx.annotation_annotation",
"framework-connectivity.impl",
"framework-connectivity-pre-jarjar",
],
jarjar_rules: ":framework-connectivity-jarjar-rules",
srcs: [
// Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.TIRAMISU),
// so that API checks are enforced for R+ users of this library
// Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.S)
// or above as appropriate so that API checks are enforced for R+ users of this library
"src/android/net/RoutingCoordinatorManager.java",
"src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java",
],
visibility: [

View File

@@ -14,6 +14,15 @@ android\.net\.INetworkAgentRegistry(\$.+)?
# TODO: move files to android.net.connectivity.visiblefortesting
android\.net\.IConnectivityDiagnosticsCallback(\$.+)?
# Classes used by tethering as a hidden API are compiled as a lib in target
# connectivity-internal-api-util. Because it's used by tethering, it can't
# be jarjared. Classes in android.net.connectivity are exempt from being
# listed here because they are already in the target package and as such
# are already not jarjared.
# Because Tethering can be installed on R without Connectivity, any use
# of these classes must be protected by a check for >= S SDK.
# It's unlikely anybody else declares a hidden class with this name ?
android\.net\.RoutingCoordinatorManager(\$.+)?
# KeepaliveUtils is used by ConnectivityManager CTS
# TODO: move into service-connectivity so framework-connectivity stops using

View File

@@ -29,6 +29,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresApi;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -6174,4 +6175,24 @@ public class ConnectivityManager {
throw e.rethrowFromSystemServer();
}
}
private static final Object sRoutingCoordinatorManagerLock = new Object();
@GuardedBy("sRoutingCoordinatorManagerLock")
private static RoutingCoordinatorManager sRoutingCoordinatorManager = null;
/** @hide */
@RequiresApi(Build.VERSION_CODES.S)
public RoutingCoordinatorManager getRoutingCoordinatorManager() {
try {
synchronized (sRoutingCoordinatorManagerLock) {
if (null == sRoutingCoordinatorManager) {
sRoutingCoordinatorManager = new RoutingCoordinatorManager(mContext,
IRoutingCoordinator.Stub.asInterface(
mService.getRoutingCoordinatorService()));
}
return sRoutingCoordinatorManager;
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -259,4 +259,6 @@ interface IConnectivityManager
void setVpnNetworkPreference(String session, in UidRange[] ranges);
void setTestLowTcpPollingTimerForKeepalive(long timeMs);
IBinder getRoutingCoordinatorService();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2023 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 android.net;
import android.net.RouteInfo;
/** @hide */
interface IRoutingCoordinator {
/**
* Add a route for specific network
*
* @param netId the network to add the route to
* @param route the route to add
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
void addRoute(int netId, in RouteInfo route);
/**
* Remove a route for specific network
*
* @param netId the network to remove the route from
* @param route the route to remove
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
void removeRoute(int netId, in RouteInfo route);
/**
* Update a route for specific network
*
* @param netId the network to update the route for
* @param route parcelable with route information
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
void updateRoute(int netId, in RouteInfo route);
/**
* Adds an interface to a network. The interface must not be assigned to any network, including
* the specified network.
*
* @param netId the network to add the interface to.
* @param iface the name of the interface to add.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
void addInterfaceToNetwork(int netId, in String iface);
/**
* Removes an interface from a network. The interface must be assigned to the specified network.
*
* @param netId the network to remove the interface from.
* @param iface the name of the interface to remove.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
void removeInterfaceFromNetwork(int netId, in String iface);
}

View File

@@ -584,7 +584,7 @@ public final class RouteInfo implements Parcelable {
}
RouteKey p = (RouteKey) o;
// No need to do anything special for scoped addresses. Inet6Address#equals does not
// consider the scope ID, but the netd route IPCs (e.g., INetd#networkAddRouteParcel)
// consider the scope ID, but the route IPCs (e.g., RoutingCoordinatorManager#addRoute)
// and the kernel ignore scoped addresses both in the prefix and in the nexthop and only
// look at RTA_OIF.
return Objects.equals(p.mDestination, mDestination)

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2023 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 android.net;
import android.content.Context;
import android.os.Build;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
/**
* 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.
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
public class RoutingCoordinatorManager {
@NonNull final Context mContext;
@NonNull final IRoutingCoordinator mService;
public RoutingCoordinatorManager(@NonNull final Context context,
@NonNull final IRoutingCoordinator service) {
mContext = context;
mService = service;
}
/**
* Add a route for specific network
*
* @param netId the network to add the route to
* @param route the route to add
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
public void addRoute(final int netId, final RouteInfo route) {
try {
mService.addRoute(netId, route);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Remove a route for specific network
*
* @param netId the network to remove the route from
* @param route the route to remove
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
public void removeRoute(final int netId, final RouteInfo route) {
try {
mService.removeRoute(netId, route);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Update a route for specific network
*
* @param netId the network to update the route for
* @param route parcelable with route information
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
public void updateRoute(final int netId, final RouteInfo route) {
try {
mService.updateRoute(netId, route);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Adds an interface to a network. The interface must not be assigned to any network, including
* the specified network.
*
* @param netId the network to add the interface to.
* @param iface the name of the interface to add.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
public void addInterfaceToNetwork(final int netId, final String iface) {
try {
mService.addInterfaceToNetwork(netId, iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes an interface from a network. The interface must be assigned to the specified network.
*
* @param netId the network to remove the interface from.
* @param iface the name of the interface to remove.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
public void removeInterfaceFromNetwork(final int netId, final String iface) {
try {
mService.removeInterfaceFromNetwork(netId, iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -18,6 +18,7 @@ package android.net.connectivity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.RoutingCoordinatorManager;
import android.os.Build;
import android.os.IBinder;
@@ -34,15 +35,28 @@ import androidx.annotation.RequiresApi;
* linter).
* @hide
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
// TODO : rename this so that it doesn't reference "Tiramisu" since it can be used in S.
@RequiresApi(Build.VERSION_CODES.S)
public class TiramisuConnectivityInternalApiUtil {
/**
* Get a service binder token for
* {@link com.android.server.connectivity.wear.CompanionDeviceManagerProxyService}.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public static IBinder getCompanionDeviceManagerProxyService(Context ctx) {
final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
return cm.getCompanionDeviceManagerProxyService();
}
/**
* Obtain a routing coordinator manager from a context, possibly cross-module.
* @param ctx the context
* @return an instance of the coordinator manager
*/
@RequiresApi(Build.VERSION_CODES.S)
public static RoutingCoordinatorManager getRoutingCoordinatorManager(Context ctx) {
final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
return cm.getRoutingCoordinatorManager();
}
}

View File

@@ -199,7 +199,6 @@ import android.net.QosFilter;
import android.net.QosSocketFilter;
import android.net.QosSocketInfo;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.TransportInfo;
@@ -330,6 +329,7 @@ import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProfileNetworkPreferenceInfo;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.RoutingCoordinatorService;
import com.android.server.connectivity.UidRangeUtils;
import com.android.server.connectivity.VpnNetworkPreferenceInfo;
import com.android.server.connectivity.wear.CompanionDeviceManagerProxyService;
@@ -493,6 +493,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@GuardedBy("mTNSLock")
private TestNetworkService mTNS;
private final CompanionDeviceManagerProxyService mCdmps;
private final RoutingCoordinatorService mRoutingCoordinatorService;
private final Object mTNSLock = new Object();
@@ -1826,6 +1827,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
mCdmps = null;
}
mRoutingCoordinatorService = new RoutingCoordinatorService(netd);
mDestroyFrozenSockets = mDeps.isAtLeastU()
&& mDeps.isFeatureEnabled(context, KEY_DESTROY_FROZEN_SOCKETS_VERSION);
mDelayDestroyFrozenSockets = mDeps.isAtLeastU()
@@ -8515,7 +8518,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (final String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.networkAddInterface(netId, iface);
mRoutingCoordinatorService.addInterfaceToNetwork(netId, iface);
wakeupModifyInterface(iface, nai, true);
mDeps.reportNetworkInterfaceForTransports(mContext, iface,
nai.networkCapabilities.getTransportTypes());
@@ -8528,45 +8531,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, nai, false);
mNetd.networkRemoveInterface(netId, iface);
mRoutingCoordinatorService.removeInterfaceFromNetwork(netId, iface);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
}
}
// TODO: move to frameworks/libs/net.
private RouteInfoParcel convertRouteInfo(RouteInfo route) {
final String nextHop;
switch (route.getType()) {
case RouteInfo.RTN_UNICAST:
if (route.hasGateway()) {
nextHop = route.getGateway().getHostAddress();
} else {
nextHop = INetd.NEXTHOP_NONE;
}
break;
case RouteInfo.RTN_UNREACHABLE:
nextHop = INetd.NEXTHOP_UNREACHABLE;
break;
case RouteInfo.RTN_THROW:
nextHop = INetd.NEXTHOP_THROW;
break;
default:
nextHop = INetd.NEXTHOP_NONE;
break;
}
final RouteInfoParcel rip = new RouteInfoParcel();
rip.ifName = route.getInterface();
rip.destination = route.getDestination().toString();
rip.nextHop = nextHop;
rip.mtu = route.getMtu();
return rip;
}
/**
* Have netd update routes from oldLp to newLp.
* @return true if routes changed between oldLp and newLp
@@ -8587,10 +8558,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
mRoutingCoordinatorService.addRoute(netId, route);
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
loge("Exception in networkAddRouteParcel for non-gateway: " + e);
loge("Exception in addRoute for non-gateway: " + e);
}
}
}
@@ -8598,10 +8569,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
mRoutingCoordinatorService.addRoute(netId, route);
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
loge("Exception in networkAddRouteParcel for gateway: " + e);
loge("Exception in addRoute for gateway: " + e);
}
}
}
@@ -8609,18 +8580,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (RouteInfo route : routeDiff.removed) {
if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
try {
mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
mRoutingCoordinatorService.removeRoute(netId, route);
} catch (Exception e) {
loge("Exception in networkRemoveRouteParcel: " + e);
loge("Exception in removeRoute: " + e);
}
}
for (RouteInfo route : routeDiff.updated) {
if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
try {
mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
mRoutingCoordinatorService.updateRoute(netId, route);
} catch (Exception e) {
loge("Exception in networkUpdateRouteParcel: " + e);
loge("Exception in updateRoute: " + e);
}
}
return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
@@ -10261,7 +10232,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If a rate limit has been configured and is applicable to this network (network
// provides internet connectivity), apply it. The tc police filter cannot be attached
// before the clsact qdisc is added which happens as part of updateLinkProperties ->
// updateInterfaces -> INetd#networkAddInterface.
// updateInterfaces -> RoutingCoordinatorManager#addInterfaceToNetwork
// Note: in case of a system server crash, the NetworkController constructor in netd
// (called when netd starts up) deletes the clsact qdisc of all interfaces.
if (canNetworkBeRateLimited(networkAgent) && mIngressRateLimit >= 0) {
@@ -12740,4 +12711,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceNetworkStackPermission(mContext);
return mCdmps;
}
@Override
public IBinder getRoutingCoordinatorService() {
enforceNetworkStackPermission(mContext);
return mRoutingCoordinatorService;
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2023 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.server.connectivity;
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
import android.annotation.NonNull;
import android.content.Context;
import android.net.INetd;
import android.net.IRoutingCoordinator;
import android.net.RouteInfo;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
/**
* Class to coordinate routing across multiple clients.
*
* At present this is just a wrapper for netd methods, but it will be used to host some more
* coordination logic in the near future. It can be used to pull up some of the routing logic
* from netd into Java land.
*
* Note that usage of this class is not thread-safe. Clients are responsible for their own
* synchronization.
*/
public class RoutingCoordinatorService extends IRoutingCoordinator.Stub {
private final INetd mNetd;
public RoutingCoordinatorService(@NonNull INetd netd) {
mNetd = netd;
}
/**
* Add a route for specific network
*
* @param netId the network to add the route to
* @param route the route to add
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
@Override
public void addRoute(final int netId, final RouteInfo route)
throws ServiceSpecificException, RemoteException {
mNetd.networkAddRouteParcel(netId, toRouteInfoParcel(route));
}
/**
* Remove a route for specific network
*
* @param netId the network to remove the route from
* @param route the route to remove
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
@Override
public void removeRoute(final int netId, final RouteInfo route)
throws ServiceSpecificException, RemoteException {
mNetd.networkRemoveRouteParcel(netId, toRouteInfoParcel(route));
}
/**
* Update a route for specific network
*
* @param netId the network to update the route for
* @param route parcelable with route information
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
@Override
public void updateRoute(final int netId, final RouteInfo route)
throws ServiceSpecificException, RemoteException {
mNetd.networkUpdateRouteParcel(netId, toRouteInfoParcel(route));
}
/**
* Adds an interface to a network. The interface must not be assigned to any network, including
* the specified network.
*
* @param netId the network to add the interface to.
* @param iface the name of the interface to add.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
@Override
public void addInterfaceToNetwork(final int netId, final String iface)
throws ServiceSpecificException, RemoteException {
mNetd.networkAddInterface(netId, iface);
}
/**
* Removes an interface from a network. The interface must be assigned to the specified network.
*
* @param netId the network to remove the interface from.
* @param iface the name of the interface to remove.
*
* @throws ServiceSpecificException in case of failure, with an error code corresponding to the
* unix errno.
*/
@Override
public void removeInterfaceFromNetwork(final int netId, final String iface)
throws ServiceSpecificException, RemoteException {
mNetd.networkRemoveInterface(netId, iface);
}
}

View File

@@ -28,6 +28,7 @@ import android.net.INetd;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.TetherConfigParcel;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -278,4 +279,38 @@ public class NetdUtils {
throw new IllegalStateException(e);
}
}
/**
* Convert a RouteInfo into a RouteInfoParcel.
*/
public static RouteInfoParcel toRouteInfoParcel(RouteInfo route) {
final String nextHop;
switch (route.getType()) {
case RouteInfo.RTN_UNICAST:
if (route.hasGateway()) {
nextHop = route.getGateway().getHostAddress();
} else {
nextHop = INetd.NEXTHOP_NONE;
}
break;
case RouteInfo.RTN_UNREACHABLE:
nextHop = INetd.NEXTHOP_UNREACHABLE;
break;
case RouteInfo.RTN_THROW:
nextHop = INetd.NEXTHOP_THROW;
break;
default:
nextHop = INetd.NEXTHOP_NONE;
break;
}
final RouteInfoParcel rip = new RouteInfoParcel();
rip.ifName = route.getInterface();
rip.destination = route.getDestination().toString();
rip.nextHop = nextHop;
rip.mtu = route.getMtu();
return rip;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 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.annotation.Nullable;
/**
* Utilities to deal with multiple SDKs in a single mainline module.
* @hide
*/
public class SdkUtil {
/**
* Holder class taking advantage of erasure to avoid reflection running into class not found
* exceptions.
*
* This is useful to store a reference to a class that might not be present at runtime when
* fields are examined through reflection. An example is the MessageUtils class, which tries
* to get all fields in a class and therefore will try to load any class for which there
* is a member. Another example would be arguments or return values of methods in tests,
* when the testing framework uses reflection to list methods and their arguments.
*
* In these cases, LateSdk<T> can be used to hide type T from reflection, since it's erased
* and it becomes a vanilla LateSdk in Java bytecode. The T still can't be instantiated at
* runtime of course, but runtime tests will avoid that.
*
* @param <T> The held type
* @hide
*/
public static class LateSdk<T> {
@Nullable public final T value;
public LateSdk(@Nullable final T value) {
this.value = value;
}
}
}