diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 7a6df88f54..f60d7a1698 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -70,13 +70,13 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.InterfaceParams; +import com.android.net.module.util.IIpv4PrefixRequest; import com.android.net.module.util.NetdUtils; import com.android.net.module.util.RoutingCoordinatorManager; import com.android.net.module.util.SharedLog; import com.android.net.module.util.SyncStateMachine.StateInfo; import com.android.net.module.util.ip.InterfaceController; import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.PrivateAddressCoordinator; import com.android.networkstack.tethering.TetheringConfiguration; import com.android.networkstack.tethering.metrics.TetheringMetrics; import com.android.networkstack.tethering.util.InterfaceSet; @@ -242,9 +242,10 @@ public class IpServer extends StateMachineShim { private final BpfCoordinator mBpfCoordinator; @NonNull private final RoutingCoordinatorManager mRoutingCoordinator; + @NonNull + private final IIpv4PrefixRequest mIpv4PrefixRequest; private final Callback mCallback; private final InterfaceController mInterfaceCtrl; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; private final String mIfaceName; private final int mInterfaceType; @@ -301,7 +302,7 @@ public class IpServer extends StateMachineShim { String ifaceName, Handler handler, int interfaceType, SharedLog log, INetd netd, @NonNull BpfCoordinator bpfCoordinator, RoutingCoordinatorManager routingCoordinatorManager, Callback callback, - TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator, + TetheringConfiguration config, TetheringMetrics tetheringMetrics, Dependencies deps) { super(ifaceName, USE_SYNC_SM ? null : handler.getLooper()); mHandler = handler; @@ -309,6 +310,12 @@ public class IpServer extends StateMachineShim { mNetd = netd; mBpfCoordinator = bpfCoordinator; mRoutingCoordinator = routingCoordinatorManager; + mIpv4PrefixRequest = new IIpv4PrefixRequest.Stub() { + @Override + public void onIpv4PrefixConflict(IpPrefix ipPrefix) throws RemoteException { + sendMessage(CMD_NOTIFY_PREFIX_CONFLICT); + } + }; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); mIfaceName = ifaceName; @@ -317,7 +324,6 @@ public class IpServer extends StateMachineShim { mUsingLegacyDhcp = config.useLegacyDhcpServer(); mP2pLeasesSubnetPrefixLength = config.getP2pLeasesSubnetPrefixLength(); mIsWifiP2pDedicatedIpEnabled = config.shouldEnableWifiP2pDedicatedIp(); - mPrivateAddressCoordinator = addressCoordinator; mDeps = deps; mTetheringMetrics = tetheringMetrics; resetLinkProperties(); @@ -395,6 +401,11 @@ public class IpServer extends StateMachineShim { return mInterfaceParams; } + @VisibleForTesting + public IIpv4PrefixRequest getIpv4PrefixRequest() { + return mIpv4PrefixRequest; + } + /** * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper * thread. @@ -643,7 +654,7 @@ public class IpServer extends StateMachineShim { // NOTE: All of configureIPv4() will be refactored out of existence // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); - mPrivateAddressCoordinator.releaseDownstream(this); + mRoutingCoordinator.releaseDownstream(mIpv4PrefixRequest); mBpfCoordinator.tetherOffloadClientClear(this); mIpv4Address = null; mStaticIpv4ServerAddr = null; @@ -714,7 +725,8 @@ public class IpServer extends StateMachineShim { if (shouldUseWifiP2pDedicatedIp()) return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); - return mPrivateAddressCoordinator.requestDownstreamAddress(this, scope, useLastAddress); + return mRoutingCoordinator.requestDownstreamAddress(mInterfaceType, scope, useLastAddress, + mIpv4PrefixRequest); } private boolean startIPv6() { diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 13b80040a5..df255f384e 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -253,7 +253,6 @@ public class Tethering { private final TetheringNotificationUpdater mNotificationUpdater; private final UserManager mUserManager; private final BpfCoordinator mBpfCoordinator; - private final PrivateAddressCoordinator mPrivateAddressCoordinator; private final TetheringMetrics mTetheringMetrics; private final WearableConnectionManager mWearableConnectionManager; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; @@ -359,7 +358,6 @@ public class Tethering { // Load tethering configuration. updateConfiguration(); mConfig.readEnableSyncSM(mContext); - mPrivateAddressCoordinator = mDeps.makePrivateAddressCoordinator(mContext); // Must be initialized after tethering configuration is loaded because BpfCoordinator // constructor needs to use the configuration. @@ -2001,11 +1999,11 @@ public class Tethering { final UpstreamNetworkState ns = (UpstreamNetworkState) o; switch (arg1) { case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - mPrivateAddressCoordinator.updateUpstreamPrefix( + mRoutingCoordinator.updateUpstreamPrefix( ns.linkProperties, ns.networkCapabilities, ns.network); break; case UpstreamNetworkMonitor.EVENT_ON_LOST: - mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); + mRoutingCoordinator.removeUpstreamPrefix(ns.network); break; } @@ -2075,7 +2073,7 @@ public class Tethering { return; } - mPrivateAddressCoordinator.maybeRemoveDeprecatedUpstreams(); + mRoutingCoordinator.maybeRemoveDeprecatedUpstreams(); mUpstreamNetworkMonitor.startObserveAllNetworks(); // TODO: De-duplicate with updateUpstreamWanted() below. @@ -2663,11 +2661,6 @@ public class Tethering { dumpBpf(pw); - pw.println("Private address coordinator:"); - pw.increaseIndent(); - mPrivateAddressCoordinator.dump(pw); - pw.decreaseIndent(); - if (mWearableConnectionManager != null) { pw.println("WearableConnectionManager:"); pw.increaseIndent(); @@ -2821,8 +2814,7 @@ public class Tethering { mLog.i("adding IpServer for: " + iface); final TetherState tetherState = new TetherState( new IpServer(iface, mHandler, interfaceType, mLog, mNetd, mBpfCoordinator, - mRoutingCoordinator, new ControlCallback(), mConfig, - mPrivateAddressCoordinator, mTetheringMetrics, + mRoutingCoordinator, new ControlCallback(), mConfig, mTetheringMetrics, mDeps.makeIpServerDependencies()), isNcm); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index cc878d5d81..a4823caac0 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -36,6 +36,7 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; +import com.android.net.module.util.PrivateAddressCoordinator; import com.android.net.module.util.RoutingCoordinatorManager; import com.android.net.module.util.RoutingCoordinatorService; import com.android.net.module.util.SharedLog; @@ -136,7 +137,10 @@ public abstract class TetheringDependencies { 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); } @@ -174,14 +178,6 @@ public abstract class TetheringDependencies { return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true"); } - /** - * 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. * diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index f7834a3a99..8f5e6c4d1c 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -89,13 +89,10 @@ import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.RoutingCoordinatorManager; -import com.android.net.module.util.SdkUtil.LateSdk; import com.android.net.module.util.SharedLog; import com.android.networkstack.tethering.BpfCoordinator; -import com.android.networkstack.tethering.PrivateAddressCoordinator; import com.android.networkstack.tethering.TetheringConfiguration; import com.android.networkstack.tethering.metrics.TetheringMetrics; import com.android.networkstack.tethering.util.InterfaceSet; @@ -175,7 +172,6 @@ public class IpServerTest { @Mock private DadProxy mDadProxy; @Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private IpServer.Dependencies mDependencies; - @Mock private PrivateAddressCoordinator mAddressCoordinator; @Mock private RoutingCoordinatorManager mRoutingCoordinatorManager; @Mock private NetworkStatsManager mStatsManager; @Mock private TetheringConfiguration mTetherConfig; @@ -261,9 +257,9 @@ public class IpServerTest { verify(mBpfCoordinator).updateIpv6UpstreamInterface( mIpServer, interfaceParams.index, upstreamPrefixes); } - reset(mNetd, mBpfCoordinator, mCallback, mAddressCoordinator); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(), - anyBoolean())).thenReturn(mTestAddress); + reset(mNetd, mBpfCoordinator, mCallback, mRoutingCoordinatorManager); + when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(), + anyBoolean(), any())).thenReturn(mTestAddress); } @SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to Thread.run() @@ -284,8 +280,8 @@ public class IpServerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(), - anyBoolean())).thenReturn(mTestAddress); + when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(), + anyBoolean(), any())).thenReturn(mTestAddress); when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(DEFAULT_USING_BPF_OFFLOAD); when(mTetherConfig.useLegacyDhcpServer()).thenReturn(false /* default value */); @@ -297,7 +293,7 @@ public class IpServerTest { mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); return new IpServer(IFACE_NAME, mHandler, interfaceType, mSharedLog, mNetd, mBpfCoordinator, - mRoutingCoordinatorManager, mCallback, mTetherConfig, mAddressCoordinator, + mRoutingCoordinatorManager, mCallback, mTetherConfig, mTetheringMetrics, mDependencies); } @@ -349,10 +345,14 @@ public class IpServerTest { initStateMachine(TETHERING_BLUETOOTH); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); + InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager); if (isAtLeastT()) { - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), - eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true)); + inOrder.verify(mRoutingCoordinatorManager) + .requestDownstreamAddress( + eq(TETHERING_BLUETOOTH), + eq(CONNECTIVITY_SCOPE_GLOBAL), + eq(true), + any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); } @@ -373,7 +373,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); + InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); @@ -384,7 +384,7 @@ public class IpServerTest { argThat(cfg -> assertContainsFlag(cfg.flags, IF_STATE_DOWN))); } inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> cfg.flags.length == 0)); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); + inOrder.verify(mRoutingCoordinatorManager).releaseDownstream(any()); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( @@ -392,7 +392,7 @@ public class IpServerTest { verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_BLUETOOTH), eq(TETHER_ERROR_NO_ERROR)); verify(mTetheringMetrics).sendReport(eq(TETHERING_BLUETOOTH)); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); + verifyNoMoreInteractions(mNetd, mCallback, mRoutingCoordinatorManager); } @Test @@ -400,9 +400,9 @@ public class IpServerTest { initStateMachine(TETHERING_USB); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), - eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true)); + InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager); + inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(), + eq(CONNECTIVITY_SCOPE_GLOBAL), eq(true), any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); @@ -414,7 +414,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); + verifyNoMoreInteractions(mNetd, mCallback, mRoutingCoordinatorManager); } @Test @@ -422,9 +422,9 @@ public class IpServerTest { initStateMachine(TETHERING_WIFI_P2P); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), - eq(CONNECTIVITY_SCOPE_LOCAL), eq(true)); + InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager); + inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(), + eq(CONNECTIVITY_SCOPE_LOCAL), eq(true), any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); @@ -436,7 +436,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); + verifyNoMoreInteractions(mNetd, mCallback, mRoutingCoordinatorManager); } @Test @@ -445,11 +445,11 @@ public class IpServerTest { true /* shouldEnableWifiP2pDedicatedIp */); dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); - InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator); + InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager); // When using WiFi P2p dedicated IP, the IpServer just picks the IP address without - // requesting for it at PrivateAddressCoordinator. - inOrder.verify(mAddressCoordinator, never()).requestDownstreamAddress(any(), anyInt(), - anyBoolean()); + // requesting for it at RoutingCoordinatorManager. + inOrder.verify(mRoutingCoordinatorManager, never()) + .requestDownstreamAddress(anyInt(), anyInt(), anyBoolean(), any()); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); @@ -463,7 +463,7 @@ public class IpServerTest { assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); assertEquals(List.of(new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS)), mLinkPropertiesCaptor.getValue().getLinkAddresses()); - verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator); + verifyNoMoreInteractions(mNetd, mCallback, mRoutingCoordinatorManager); } @Test @@ -569,15 +569,9 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); clearInvocations( - mNetd, mCallback, mAddressCoordinator, mBpfCoordinator, mRoutingCoordinatorManager); + mNetd, mCallback, mBpfCoordinator, mRoutingCoordinatorManager); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = - inOrder( - mNetd, - mCallback, - mAddressCoordinator, - mBpfCoordinator, - mRoutingCoordinatorManager); + InOrder inOrder = inOrder(mNetd, mCallback, mBpfCoordinator, mRoutingCoordinatorManager); inOrder.verify(mBpfCoordinator).maybeDetachProgram(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mRoutingCoordinatorManager) .removeInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); @@ -592,15 +586,14 @@ public class IpServerTest { inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg( argThat(cfg -> IFACE_NAME.equals(cfg.ifName))); - inOrder.verify(mAddressCoordinator).releaseDownstream(any()); + inOrder.verify(mRoutingCoordinatorManager).releaseDownstream(any()); inOrder.verify(mBpfCoordinator).tetherOffloadClientClear(mIpServer); inOrder.verify(mBpfCoordinator).removeIpServer(mIpServer); inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions( - mNetd, mCallback, mAddressCoordinator, mBpfCoordinator, mRoutingCoordinatorManager); + verifyNoMoreInteractions(mNetd, mCallback, mRoutingCoordinatorManager, mBpfCoordinator); } @Test @@ -737,9 +730,9 @@ public class IpServerTest { final ArgumentCaptor lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); - InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), - eq(CONNECTIVITY_SCOPE_LOCAL), eq(true)); + InOrder inOrder = inOrder(mNetd, mCallback, mRoutingCoordinatorManager); + inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(), + eq(CONNECTIVITY_SCOPE_LOCAL), eq(true), 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), @@ -747,18 +740,18 @@ public class IpServerTest { inOrder.verify(mCallback).updateInterfaceState( mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture()); - verifyNoMoreInteractions(mCallback, mAddressCoordinator); + verifyNoMoreInteractions(mCallback, mRoutingCoordinatorManager); // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals // onNewPrefixRequest callback. final LinkAddress newAddress = new LinkAddress("192.168.100.125/24"); - when(mAddressCoordinator.requestDownstreamAddress(any(), anyInt(), - anyBoolean())).thenReturn(newAddress); + when(mRoutingCoordinatorManager.requestDownstreamAddress(anyInt(), anyInt(), + anyBoolean(), any())).thenReturn(newAddress); eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24")); mLooper.dispatchAll(); - inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), - eq(CONNECTIVITY_SCOPE_LOCAL), eq(false)); + inOrder.verify(mRoutingCoordinatorManager).requestDownstreamAddress(anyInt(), + eq(CONNECTIVITY_SCOPE_LOCAL), eq(false), 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 bff1fdaec3..1ab5766113 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -24,8 +24,9 @@ import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; +import static android.net.ip.IpServer.CMD_NOTIFY_PREFIX_CONFLICT; -import static com.android.networkstack.tethering.PrivateAddressCoordinator.TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION; +import static com.android.net.module.util.PrivateAddressCoordinator.TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION; import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix; import static org.junit.Assert.assertEquals; @@ -34,6 +35,9 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -47,10 +51,14 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.ip.IpServer; +import android.os.IBinder; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.net.module.util.IIpv4PrefixRequest; +import com.android.net.module.util.PrivateAddressCoordinator; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -92,12 +100,26 @@ public final class PrivateAddressCoordinatorTest { new IpPrefix("172.16.0.0/12"), new IpPrefix("10.0.0.0/8"))); + private void setUpIpServer(IpServer ipServer, int interfaceType) throws Exception { + when(ipServer.interfaceType()).thenReturn(interfaceType); + final IIpv4PrefixRequest request = mock(IIpv4PrefixRequest.class); + when(ipServer.getIpv4PrefixRequest()).thenReturn(request); + when(request.asBinder()).thenReturn(mock(IBinder.class)); + doAnswer( + invocation -> { + ipServer.sendMessage(CMD_NOTIFY_PREFIX_CONFLICT); + return null; + }) + .when(request) + .onIpv4PrefixConflict(any()); + } + private void setUpIpServers() throws Exception { - when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); - when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); - when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); - when(mLocalHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); - when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); + setUpIpServer(mUsbIpServer, TETHERING_USB); + setUpIpServer(mEthernetIpServer, TETHERING_ETHERNET); + setUpIpServer(mHotspotIpServer, TETHERING_WIFI); + setUpIpServer(mLocalHotspotIpServer, TETHERING_WIFI); + setUpIpServer(mWifiP2pIpServer, TETHERING_WIFI_P2P); } @Before @@ -112,14 +134,22 @@ public final class PrivateAddressCoordinatorTest { spy(new PrivateAddressCoordinator(mConnectivityMgr::getAllNetworks, mDeps)); } - private LinkAddress requestDownstreamAddress(final IpServer ipServer, int scope, - boolean useLastAddress) { - final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - ipServer, scope, useLastAddress); + private LinkAddress requestDownstreamAddress( + final IpServer ipServer, int scope, boolean useLastAddress) throws Exception { + final LinkAddress address = + mPrivateAddressCoordinator.requestDownstreamAddress( + ipServer.interfaceType(), + scope, + useLastAddress, + ipServer.getIpv4PrefixRequest()); when(ipServer.getAddress()).thenReturn(address); return address; } + private void releaseDownstream(final IpServer ipServer) { + mPrivateAddressCoordinator.releaseDownstream(ipServer.getIpv4PrefixRequest()); + } + private void updateUpstreamPrefix(UpstreamNetworkState ns) { mPrivateAddressCoordinator.updateUpstreamPrefix( ns.linkProperties, ns.networkCapabilities, ns.network); @@ -145,8 +175,8 @@ public final class PrivateAddressCoordinatorTest { assertNotEquals(usbPrefix, bluetoothPrefix); assertNotEquals(usbPrefix, newHotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); + releaseDownstream(mHotspotIpServer); + releaseDownstream(mUsbIpServer); } @Test @@ -158,7 +188,7 @@ public final class PrivateAddressCoordinatorTest { CONNECTIVITY_SCOPE_GLOBAL, false /* useLastAddress */); final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress); assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + releaseDownstream(mHotspotIpServer); // - Test previous enabled hotspot prefix(cached prefix) is reserved. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( @@ -168,7 +198,7 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix usbPrefix = asIpPrefix(usbAddress); assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix); assertNotEquals(hotspotPrefix, usbPrefix); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); + releaseDownstream(mUsbIpServer); // - Test wifi p2p prefix is reserved. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( @@ -179,7 +209,7 @@ public final class PrivateAddressCoordinatorTest { assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix); assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix); assertNotEquals(hotspotPrefix, etherPrefix); - mPrivateAddressCoordinator.releaseDownstream(mEthernetIpServer); + releaseDownstream(mEthernetIpServer); } @Test @@ -190,8 +220,8 @@ public final class PrivateAddressCoordinatorTest { final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); + releaseDownstream(mHotspotIpServer); + releaseDownstream(mUsbIpServer); final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer, CONNECTIVITY_SCOPE_GLOBAL, true /* useLastAddress */); @@ -254,10 +284,11 @@ public final class PrivateAddressCoordinatorTest { } private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception { - verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - mPrivateAddressCoordinator.releaseDownstream(ipServer); + verify(ipServer).sendMessage(CMD_NOTIFY_PREFIX_CONFLICT); + releaseDownstream(ipServer); + final int interfaceType = ipServer.interfaceType(); reset(ipServer); - setUpIpServers(); + setUpIpServer(ipServer, interfaceType); } private int getSubAddress(final byte... ipv4Address) { @@ -273,7 +304,7 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix hotspotPrefix = asIpPrefix(address); final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress); assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); - mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + releaseDownstream(mHotspotIpServer); } @Test diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 66fe9571a5..d0c036f677 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -71,7 +71,6 @@ import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH; import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; -import static com.android.networkstack.tethering.PrivateAddressCoordinator.TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION; import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST; import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST; import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener; @@ -97,6 +96,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; @@ -192,7 +192,9 @@ 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.PrivateAddressCoordinator; import com.android.net.module.util.RoutingCoordinatorManager; +import com.android.net.module.util.RoutingCoordinatorService; import com.android.net.module.util.SharedLog; import com.android.net.module.util.ip.IpNeighborMonitor; import com.android.networkstack.apishim.common.BluetoothPanShim; @@ -293,7 +295,6 @@ public class TetheringTest { @Mock private BluetoothPanShim mBluetoothPanShim; @Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim; @Mock private TetheringMetrics mTetheringMetrics; - @Mock private RoutingCoordinatorManager mRoutingCoordinatorManager; @Mock private PrivateAddressCoordinator.Dependencies mPrivateAddressCoordinatorDependencies; private final MockIpServerDependencies mIpServerDependencies = @@ -318,12 +319,12 @@ public class TetheringTest { private TetheringConfiguration mConfig; private EntitlementManager mEntitleMgr; private OffloadController mOffloadCtrl; - private PrivateAddressCoordinator mPrivateAddressCoordinator; private SoftApCallback mSoftApCallback; private SoftApCallback mLocalOnlyHotspotCallback; private UpstreamNetworkMonitor mUpstreamNetworkMonitor; private UpstreamNetworkMonitor.EventListener mEventListener; private TetheredInterfaceCallbackShim mTetheredInterfaceCallbackShim; + private RoutingCoordinatorManager mRoutingCoordinatorManager; private TestConnectivityManager mCm; private boolean mForceEthernetServiceUnavailable = false; @@ -489,8 +490,16 @@ public class TetheringTest { } @Override - public RoutingCoordinatorManager getRoutingCoordinator(final Context context, - SharedLog log) { + public RoutingCoordinatorManager getRoutingCoordinator( + final Context context, SharedLog log) { + ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); + when(mPrivateAddressCoordinatorDependencies.isFeatureEnabled(anyString())) + .thenReturn(false); + RoutingCoordinatorService service = new RoutingCoordinatorService( + getINetd(context, log), + cm::getAllNetworks, + mPrivateAddressCoordinatorDependencies); + mRoutingCoordinatorManager = spy(new RoutingCoordinatorManager(context, service)); return mRoutingCoordinatorManager; } @@ -536,15 +545,6 @@ public class TetheringTest { return mTetheringMetrics; } - @Override - public PrivateAddressCoordinator makePrivateAddressCoordinator(Context ctx) { - ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class); - mPrivateAddressCoordinator = - new PrivateAddressCoordinator( - cm::getAllNetworks, mPrivateAddressCoordinatorDependencies); - return mPrivateAddressCoordinator; - } - @Override public BluetoothPanShim makeBluetoothPanShim(BluetoothPan pan) { try { @@ -668,8 +668,6 @@ public class TetheringTest { .thenReturn(true); initOffloadConfiguration(OFFLOAD_HAL_VERSION_HIDL_1_0, 0 /* defaultDisabled */); when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); - when(mPrivateAddressCoordinatorDependencies.isFeatureEnabled(anyString())) - .thenReturn(false); mServiceContext = new TestContext(mContext); mServiceContext.setUseRegisteredHandlers(true); @@ -687,6 +685,7 @@ public class TetheringTest { new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mCm = spy(new TestConnectivityManager(mServiceContext, mock(IConnectivityManager.class))); + when(mCm.getAllNetworks()).thenReturn(new Network[] {}); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true); @@ -870,6 +869,9 @@ public class TetheringTest { assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue())); } + // Ignore calls to {@link ConnectivityManager#getallNetworks}. + verify(mCm, atLeast(0)).getAllNetworks(); + // The default network request is only ever filed once. verifyNoMoreInteractions(mCm); } diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index cb62ae1b45..a04ebdd532 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -2028,7 +2028,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mCdmps = null; } - mRoutingCoordinatorService = new RoutingCoordinatorService(netd); + mRoutingCoordinatorService = + new RoutingCoordinatorService(netd, this::getAllNetworks, mContext); mMulticastRoutingCoordinatorService = mDeps.makeMulticastRoutingCoordinatorService(mHandler); diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp index 9d27608b97..66e1dad337 100644 --- a/staticlibs/Android.bp +++ b/staticlibs/Android.bp @@ -650,6 +650,7 @@ filegroup { name: "net-utils-all-srcs", srcs: [ "device/**/*.java", + ":framework-connectivity-shared-srcs", ":net-utils-framework-common-srcs", ], visibility: ["//visibility:private"], 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 0000000000..cc1c19cffa --- /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 72a4a94720..097824f331 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 @@ interface IRoutingCoordinator { * 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/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java similarity index 82% rename from Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java rename to staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java index 50f82cf0ec..990358d39e 100644 --- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/staticlibs/device/com/android/net/module/util/PrivateAddressCoordinator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.networkstack.tethering; +package com.android.net.module.util; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; @@ -24,7 +24,6 @@ import static android.net.TetheringManager.TETHERING_WIFI_P2P; 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.LinkAddress; 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.ArrayList; 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 @@ import java.util.function.Supplier; * 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 @@ public class PrivateAddressCoordinator { // when tethering is down. Instead tethering would remove all deprecated upstreams from // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams(). private final ArrayMap> mUpstreamPrefixMap; - private final ArraySet mDownstreams; + // The downstreams are indexed by Ipv4PrefixRequest, which is a wrapper of the Binder object of + // IIpv4PrefixRequest. + private final ArrayMap 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 mTetheringPrefixes; @@ -116,7 +116,7 @@ public class PrivateAddressCoordinator { @VisibleForTesting public PrivateAddressCoordinator(Supplier getAllNetworksSupplier, Dependencies deps) { - mDownstreams = new ArraySet<>(); + mDownstreams = new ArrayMap<>(); mUpstreamPrefixMap = new ArrayMap<>(); mGetAllNetworksSupplier = getAllNetworksSupplier; mDeps = deps; @@ -168,12 +168,18 @@ public class PrivateAddressCoordinator { } private void handleMaybePrefixConflict(final List prefixes) { - for (IpServer downstream : mDownstreams) { - final IpPrefix target = getDownstreamPrefix(downstream); + for (Map.Entry 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 @@ public class PrivateAddressCoordinator { 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 @@ public class PrivateAddressCoordinator { (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 @@ public class PrivateAddressCoordinator { } /** 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 @@ public class PrivateAddressCoordinator { // 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 @@ public class PrivateAddressCoordinator { 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 @@ public class PrivateAddressCoordinator { } } + // TODO: dump PrivateAddressCoordinator when dumping RoutingCoordinatorService. void dump(final IndentingPrintWriter pw) { pw.println("mTetheringPrefixes:"); pw.increaseIndent(); @@ -429,8 +481,8 @@ public class PrivateAddressCoordinator { 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/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java index 02e3643ef1..9ea0947efb 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 @@ public class RoutingCoordinatorManager { 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 c75b860117..d16c234859 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 @@ package com.android.net.module.util; 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.ArraySet; 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 @@ public class RoutingCoordinatorService extends IRoutingCoordinator.Stub { 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 getAllNetworksSupplier, + @NonNull Context context) { + this(netd, getAllNetworksSupplier, new PrivateAddressCoordinator.Dependencies(context)); + } + + @VisibleForTesting + public RoutingCoordinatorService(@NonNull INetd netd, + @NonNull Supplier getAllNetworksSupplier, + @NonNull PrivateAddressCoordinator.Dependencies pacDeps) { mNetd = netd; + mPrivateAddressCoordinator = new PrivateAddressCoordinator(getAllNetworksSupplier, pacDeps); } /** @@ -225,4 +246,74 @@ public class RoutingCoordinatorService extends IRoutingCoordinator.Stub { } } } + + // 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); + } + }); + } } diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt index b04561c8f0..035ce0fe12 100644 --- a/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt +++ b/staticlibs/tests/unit/src/com/android/net/module/util/RoutingCoordinatorServiceTest.kt @@ -16,7 +16,9 @@ package com.android.net.module.util +import android.content.Context import android.net.INetd +import android.net.Network import android.os.Build import android.util.Log import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo @@ -34,7 +36,9 @@ import org.mockito.Mockito.mock @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) class RoutingCoordinatorServiceTest { val mNetd = mock(INetd::class.java) - val mService = RoutingCoordinatorService(mNetd) + val mGetAllNetworksSupplier = { emptyArray() } + val mContext = mock(Context::class.java) + val mService = RoutingCoordinatorService(mNetd, mGetAllNetworksSupplier, mContext) @Test fun testInterfaceForward() {