import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
+ private static enum UpdateType { IF_NEEDED, FORCE };
+
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
updateStatsForAllUpstreams();
forceTetherStatsPoll();
// [2] (Re)Push all state.
- // TODO: computeAndPushLocalPrefixes()
- // TODO: push all downstream state.
+ computeAndPushLocalPrefixes(UpdateType.FORCE);
+ pushAllDownstreamState();
pushUpstreamParameters(null);
}
}
private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occuring on this upstream.
+ // setDataLimit may only be called while offload is occurring on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
return true;
}
// upstream parameters fails (probably just wait for a subsequent
// onOffloadEvent() callback to tell us offload is available again and
// then reapply all state).
- computeAndPushLocalPrefixes();
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
pushUpstreamParameters(prevUpstream);
}
public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
- if (!started()) return;
-
mExemptPrefixes = localPrefixes;
- computeAndPushLocalPrefixes();
+
+ if (!started()) return;
+ computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
}
public void notifyDownstreamLinkProperties(LinkProperties lp) {
if (Objects.equals(oldLp, lp)) return;
if (!started()) return;
+ pushDownstreamState(oldLp, lp);
+ }
- final List<RouteInfo> oldRoutes = (oldLp != null) ? oldLp.getRoutes() : new ArrayList<>();
- final List<RouteInfo> newRoutes = lp.getRoutes();
+ private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) {
+ final String ifname = newLp.getInterfaceName();
+ final List<RouteInfo> oldRoutes =
+ (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST;
+ final List<RouteInfo> newRoutes = newLp.getRoutes();
// For each old route, if not in new routes: remove.
- for (RouteInfo oldRoute : oldRoutes) {
- if (shouldIgnoreDownstreamRoute(oldRoute)) continue;
- if (!newRoutes.contains(oldRoute)) {
- mHwInterface.removeDownstreamPrefix(ifname, oldRoute.getDestination().toString());
+ for (RouteInfo ri : oldRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!newRoutes.contains(ri)) {
+ mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
// For each new route, if not in old routes: add.
- for (RouteInfo newRoute : newRoutes) {
- if (shouldIgnoreDownstreamRoute(newRoute)) continue;
- if (!oldRoutes.contains(newRoute)) {
- mHwInterface.addDownstreamPrefix(ifname, newRoute.getDestination().toString());
+ for (RouteInfo ri : newRoutes) {
+ if (shouldIgnoreDownstreamRoute(ri)) continue;
+ if (!oldRoutes.contains(ri)) {
+ mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString());
}
}
}
+ private void pushAllDownstreamState() {
+ for (LinkProperties lp : mDownstreams.values()) {
+ pushDownstreamState(null, lp);
+ }
+ }
+
public void removeDownstreamInterface(String ifname) {
final LinkProperties lp = mDownstreams.remove(ifname);
if (lp == null) return;
return success;
}
- private boolean computeAndPushLocalPrefixes() {
+ private boolean computeAndPushLocalPrefixes(UpdateType how) {
+ final boolean force = (how == UpdateType.FORCE);
final Set<String> localPrefixStrs = computeLocalPrefixStrings(
mExemptPrefixes, mUpstreamLinkProperties);
- if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
+ if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
mLastLocalPrefixStrs = localPrefixStrs;
return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
offload.setUpstreamLinkProperties(upstreamLp);
}
+ // Pretend that some local prefixes and downstreams have been added
+ // (and removed, for good measure).
+ final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>();
+ for (String s : new String[]{
+ "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) {
+ minimumLocalPrefixes.add(new IpPrefix(s));
+ }
+ offload.setLocalPrefixes(minimumLocalPrefixes);
+
+ final LinkProperties usbLinkProperties = new LinkProperties();
+ usbLinkProperties.setInterfaceName(RNDIS0);
+ usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
+ usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+ offload.notifyDownstreamLinkProperties(usbLinkProperties);
+
+ final LinkProperties wifiLinkProperties = new LinkProperties();
+ wifiLinkProperties.setInterfaceName(WLAN0);
+ wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24"));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(WIFI_PREFIX)));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+ // Use a benchmark prefix (RFC 5180 + erratum), since the documentation
+ // prefix is included in the excluded prefix list.
+ wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64"));
+ wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64"));
+ wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix("2001:2::/64")));
+ offload.notifyDownstreamLinkProperties(wifiLinkProperties);
+
+ offload.removeDownstreamInterface(RNDIS0);
+
// Clear invocation history, especially the getForwardedStats() calls
// that happen with setUpstreamParameters().
clearInvocations(mHardware);
verifyNoMoreInteractions(mNMService);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
+ verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
+ ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
+ assertEquals(4, localPrefixes.size());
+ assertArrayListContains(localPrefixes,
+ // TODO: The logic to find and exclude downstream IP prefixes
+ // is currently in Tethering's OffloadWrapper but must be moved
+ // into OffloadController proper. After this, also check for:
+ // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128"
+ "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64");
+ verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24");
+ verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64");
verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
verifyNoMoreInteractions(mHardware);
private static void assertArrayListContains(ArrayList<String> list, String... elems) {
for (String element : elems) {
- assertTrue(list.contains(element));
+ assertTrue(element + " not in list", list.contains(element));
}
}
}