diff options
3 files changed, 163 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index c64e70516abc..cb50e9fdbfed 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -20,10 +20,15 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.util.SharedLog; import android.os.Handler; import android.provider.Settings; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.ArrayList; + /** * A class to encapsulate the business logic of programming the tethering * hardware offload interface. @@ -92,8 +97,12 @@ public class OffloadController { public void setUpstreamLinkProperties(LinkProperties lp) { if (!started()) return; - // TODO: setUpstreamParameters(). - mUpstreamLinkProperties = lp; + mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; + // TODO: examine return code and decide what to do if programming + // upstream parameters fails (probably just wait for a subsequent + // onOffloadEvent() callback to tell us offload is available again and + // then reapply all state). + pushUpstreamParameters(); } // TODO: public void addDownStream(...) @@ -106,4 +115,40 @@ public class OffloadController { private boolean started() { return mConfigInitialized && mControlInitialized; } + + private boolean pushUpstreamParameters() { + if (mUpstreamLinkProperties == null) { + return mHwInterface.setUpstreamParameters(null, null, null, null); + } + + // A stacked interface cannot be an upstream for hardware offload. + // Consequently, we examine only the primary interface name, look at + // getAddresses() rather than getAllAddresses(), and check getRoutes() + // rather than getAllRoutes(). + final String iface = mUpstreamLinkProperties.getInterfaceName(); + final ArrayList<String> v6gateways = new ArrayList<>(); + String v4addr = null; + String v4gateway = null; + + for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { + if (ip instanceof Inet4Address) { + v4addr = ip.getHostAddress(); + break; + } + } + + // Find the gateway addresses of all default routes of either address family. + for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { + if (!ri.hasGateway()) continue; + + final String gateway = ri.getGateway().getHostAddress(); + if (ri.isIPv4Default()) { + v4gateway = gateway; + } else if (ri.isIPv6Default()) { + v6gateways.add(gateway); + } + } + + return mHwInterface.setUpstreamParameters(iface, v4addr, v4gateway, v6gateways); + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 0429ab3dca92..3ecf0d1d0c71 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -23,6 +23,8 @@ import android.os.Handler; import android.os.RemoteException; import android.net.util.SharedLog; +import java.util.ArrayList; + /** * Capture tethering dependencies, for injection. @@ -103,6 +105,25 @@ public class OffloadHardwareInterface { mControlCallback = null; } + public boolean setUpstreamParameters( + String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { + final CbResults results = new CbResults(); + try { + mOffloadControl.setUpstreamParameters( + iface, v4addr, v4gateway, v6gws, + (boolean success, String errMsg) -> { + results.success = success; + results.errMsg = errMsg; + }); + } catch (RemoteException e) { + mLog.e("failed to setUpstreamParameters: " + e); + return false; + } + + if (!results.success) mLog.e("setUpstreamParameters failed: " + results.errMsg); + return results.success; + } + private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { public final Handler handler; public final ControlCallback controlCb; diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index fb7971e1e104..c535c455e7a8 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -17,15 +17,22 @@ package com.android.server.connectivity.tethering; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.content.Context; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.util.SharedLog; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -35,9 +42,13 @@ import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; import com.android.internal.util.test.FakeSettingsProvider; +import java.net.InetAddress; +import java.util.ArrayList; + import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -49,6 +60,7 @@ public class OffloadControllerTest { @Mock private OffloadHardwareInterface mHardware; @Mock private Context mContext; + final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { @@ -114,4 +126,87 @@ public class OffloadControllerTest { inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); inOrder.verifyNoMoreInteractions(); } + + @Test + public void testSetUpstreamLinkPropertiesWorking() throws Exception { + setupFunctioningHardwareInterface(); + final OffloadController offload = + new OffloadController(null, mHardware, mContentResolver, new SharedLog("test")); + offload.start(); + + final InOrder inOrder = inOrder(mHardware); + inOrder.verify(mHardware, times(1)).initOffloadConfig(); + inOrder.verify(mHardware, times(1)).initOffloadControl( + any(OffloadHardwareInterface.ControlCallback.class)); + inOrder.verifyNoMoreInteractions(); + + offload.setUpstreamLinkProperties(null); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(null), eq(null), eq(null), eq(null)); + inOrder.verifyNoMoreInteractions(); + reset(mHardware); + + final LinkProperties lp = new LinkProperties(); + + final String testIfName = "rmnet_data17"; + lp.setInterfaceName(testIfName); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(null), eq(null), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv4Addr = "192.0.2.5"; + final String linkAddr = ipv4Addr + "/24"; + lp.addLinkAddress(new LinkAddress(linkAddr)); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(null), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv4Gateway = "192.0.2.1"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv6Gw1 = "fe80::cafe"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + ArrayList<String> v6gws = mStringArrayCaptor.getValue(); + assertEquals(1, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + inOrder.verifyNoMoreInteractions(); + + final String ipv6Gw2 = "fe80::d00d"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + + final LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName("stacked"); + stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); + stacked.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); + assertTrue(lp.addStackedLink(stacked)); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + } } |