Merge "Program offload-exempt local prefixes into the HAL"

am: de837a97aa

Change-Id: I741b6e31fb3adc922f47df824188786ff35e3aa9
This commit is contained in:
Erik Kline
2017-07-13 09:00:56 +00:00
committed by android-build-merger
8 changed files with 323 additions and 105 deletions

View File

@@ -57,6 +57,7 @@ import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
import android.os.Binder;
@@ -216,10 +217,10 @@ public class Tethering extends BaseNetworkObserver {
mContext.getContentResolver(),
mLog);
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK );
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
mSimChange = new SimChangeListener(
mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
mContext, smHandler, () -> reevaluateSimCardProvisioning());
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
@@ -227,13 +228,13 @@ public class Tethering extends BaseNetworkObserver {
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
// load device config info
updateConfiguration();
@@ -1142,12 +1143,6 @@ public class Tethering extends BaseNetworkObserver {
}
}
private void startOffloadController() {
mOffloadController.start();
mOffloadController.updateExemptPrefixes(
mUpstreamNetworkMonitor.getOffloadExemptPrefixes());
}
class TetherMasterSM extends StateMachine {
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
@@ -1165,14 +1160,14 @@ public class Tethering extends BaseNetworkObserver {
static final int CMD_CLEAR_ERROR = BASE_MASTER + 6;
static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7;
private State mInitialState;
private State mTetherModeAliveState;
private final State mInitialState;
private final State mTetherModeAliveState;
private State mSetIpForwardingEnabledErrorState;
private State mSetIpForwardingDisabledErrorState;
private State mStartTetheringErrorState;
private State mStopTetheringErrorState;
private State mSetDnsForwardersErrorState;
private final State mSetIpForwardingEnabledErrorState;
private final State mSetIpForwardingDisabledErrorState;
private final State mStartTetheringErrorState;
private final State mStopTetheringErrorState;
private final State mSetDnsForwardersErrorState;
// This list is a little subtle. It contains all the interfaces that currently are
// requesting tethering, regardless of whether these interfaces are still members of
@@ -1212,22 +1207,46 @@ public class Tethering extends BaseNetworkObserver {
mNotifyList = new ArrayList<>();
mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
setInitialState(mInitialState);
}
private void startOffloadController() {
mOffloadController.start();
sendOffloadExemptPrefixes();
}
private void sendOffloadExemptPrefixes() {
sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes());
}
private void sendOffloadExemptPrefixes(Set<IpPrefix> localPrefixes) {
// Add in well-known minimum set.
PrefixUtils.addNonForwardablePrefixes(localPrefixes);
// Add tragically hardcoded prefixes.
localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX);
// Add prefixes for all downstreams, regardless of IP serving mode.
for (TetherInterfaceStateMachine tism : mNotifyList) {
localPrefixes.addAll(PrefixUtils.localPrefixesFrom(tism.linkProperties()));
}
mOffloadController.setLocalPrefixes(localPrefixes);
}
class InitialState extends State {
@Override
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
transitionTo(mTetherModeAliveState);
break;
case EVENT_IFACE_SERVING_STATE_INACTIVE:
who = (TetherInterfaceStateMachine)message.obj;
who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
break;
@@ -1422,8 +1441,8 @@ public class Tethering extends BaseNetworkObserver {
}
private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) {
mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o);
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
sendOffloadExemptPrefixes((Set<IpPrefix>) o);
return;
}
@@ -1527,7 +1546,7 @@ public class Tethering extends BaseNetworkObserver {
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
@@ -1541,7 +1560,7 @@ public class Tethering extends BaseNetworkObserver {
break;
}
case EVENT_IFACE_SERVING_STATE_INACTIVE: {
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
@@ -1573,6 +1592,9 @@ public class Tethering extends BaseNetworkObserver {
mOffloadController.notifyDownstreamLinkProperties(newLp);
} else {
mOffloadController.removeDownstreamInterface(newLp.getInterfaceName());
// Another interface might be in local-only hotspot mode;
// resend all local prefixes to the OffloadController.
sendOffloadExemptPrefixes();
}
break;
}
@@ -1614,7 +1636,7 @@ public class Tethering extends BaseNetworkObserver {
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
who.sendMessage(mErrorNotification);
break;
case CMD_CLEAR_ERROR:

View File

@@ -20,6 +20,7 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import android.content.ContentResolver;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
import android.net.util.SharedLog;
@@ -27,8 +28,11 @@ import android.os.Handler;
import android.provider.Settings;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
@@ -47,7 +51,13 @@ public class OffloadController {
private boolean mConfigInitialized;
private boolean mControlInitialized;
private LinkProperties mUpstreamLinkProperties;
// The complete set of offload-exempt prefixes passed in via Tethering from
// all upstream and downstream sources.
private Set<IpPrefix> mExemptPrefixes;
// A strictly "smaller" set of prefixes, wherein offload-approved prefixes
// (e.g. downstream on-link prefixes) have been removed and replaced with
// prefixes representing only the locally-assigned IP addresses.
private Set<String> mLastLocalPrefixStrs;
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, SharedLog log) {
@@ -55,6 +65,8 @@ public class OffloadController {
mHwInterface = hwi;
mContentResolver = contentResolver;
mLog = log.forSubComponent(TAG);
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
}
public void start() {
@@ -134,25 +146,22 @@ public class OffloadController {
}
public void setUpstreamLinkProperties(LinkProperties lp) {
if (!started()) return;
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
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).
computeAndPushLocalPrefixes();
pushUpstreamParameters();
}
public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) {
public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
if (!started()) return;
mExemptPrefixes = exemptPrefixes;
// TODO:
// - add IP addresses from all downstream link properties
// - add routes from all non-tethering downstream link properties
// - remove any 64share prefixes
// - push this to the HAL
mExemptPrefixes = localPrefixes;
computeAndPushLocalPrefixes();
}
public void notifyDownstreamLinkProperties(LinkProperties lp) {
@@ -215,4 +224,42 @@ public class OffloadController {
return mHwInterface.setUpstreamParameters(
iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
}
private boolean computeAndPushLocalPrefixes() {
final Set<String> localPrefixStrs = computeLocalPrefixStrings(
mExemptPrefixes, mUpstreamLinkProperties);
if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
mLastLocalPrefixStrs = localPrefixStrs;
return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
}
// TODO: Factor in downstream LinkProperties once that information is available.
private static Set<String> computeLocalPrefixStrings(
Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) {
// Create an editable copy.
final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes);
// TODO: If a downstream interface (not currently passed in) is reusing
// the /64 of the upstream (64share) then:
//
// [a] remove that /64 from the local prefixes
// [b] add in /128s for IP addresses on the downstream interface
// [c] add in /128s for IP addresses on the upstream interface
//
// Until downstream information is available here, simply add /128s from
// the upstream network; they'll just be redundant with their /64.
if (upstreamLinkProperties != null) {
for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) {
if (!linkAddr.isGlobalPreferred()) continue;
final InetAddress ip = linkAddr.getAddress();
if (!(ip instanceof Inet6Address)) continue;
prefixSet.add(new IpPrefix(ip, 128));
}
}
final HashSet<String> localPrefixStrs = new HashSet<>();
for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString());
return localPrefixStrs;
}
}

View File

@@ -168,6 +168,26 @@ public class OffloadHardwareInterface {
return stats;
}
public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
final String logmsg = String.format("setLocalPrefixes([%s])",
String.join(",", localPrefixes));
final CbResults results = new CbResults();
try {
mOffloadControl.setLocalPrefixes(localPrefixes,
(boolean success, String errMsg) -> {
results.success = success;
results.errMsg = errMsg;
});
} catch (RemoteException e) {
record(logmsg, e);
return false;
}
record(logmsg, results);
return results.success;
}
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
iface = (iface != null) ? iface : NO_INTERFACE_NAME;

View File

@@ -161,6 +161,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
public int lastError() { return mLastError; }
public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }

View File

@@ -34,6 +34,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.util.NetworkConstants;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.util.Log;
@@ -72,16 +73,11 @@ public class UpstreamNetworkMonitor {
private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = {
prefix("127.0.0.0/8"), prefix("169.254.0.0/16"),
prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"),
};
public static final int EVENT_ON_AVAILABLE = 1;
public static final int EVENT_ON_CAPABILITIES = 2;
public static final int EVENT_ON_LINKPROPERTIES = 3;
public static final int EVENT_ON_LOST = 4;
public static final int NOTIFY_EXEMPT_PREFIXES = 10;
public static final int NOTIFY_LOCAL_PREFIXES = 10;
private static final int CALLBACK_LISTEN_ALL = 1;
private static final int CALLBACK_TRACK_DEFAULT = 2;
@@ -93,7 +89,7 @@ public class UpstreamNetworkMonitor {
private final Handler mHandler;
private final int mWhat;
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
private HashSet<IpPrefix> mOffloadExemptPrefixes;
private HashSet<IpPrefix> mLocalPrefixes;
private ConnectivityManager mCM;
private NetworkCallback mListenAllCallback;
private NetworkCallback mDefaultNetworkCallback;
@@ -107,7 +103,7 @@ public class UpstreamNetworkMonitor {
mHandler = mTarget.getHandler();
mLog = log.forSubComponent(TAG);
mWhat = what;
mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
mLocalPrefixes = new HashSet<>();
}
@VisibleForTesting
@@ -223,8 +219,8 @@ public class UpstreamNetworkMonitor {
return typeStatePair.ns;
}
public Set<IpPrefix> getOffloadExemptPrefixes() {
return (Set<IpPrefix>) mOffloadExemptPrefixes.clone();
public Set<IpPrefix> getLocalPrefixes() {
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
private void handleAvailable(int callbackType, Network network) {
@@ -360,11 +356,11 @@ public class UpstreamNetworkMonitor {
notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
}
private void recomputeOffloadExemptPrefixes() {
final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) {
mOffloadExemptPrefixes = exemptPrefixes;
notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone());
private void recomputeLocalPrefixes() {
final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
if (!mLocalPrefixes.equals(localPrefixes)) {
mLocalPrefixes = localPrefixes;
notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
}
}
@@ -402,7 +398,7 @@ public class UpstreamNetworkMonitor {
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
handleLinkProp(network, newLp);
recomputeOffloadExemptPrefixes();
recomputeLocalPrefixes();
}
// TODO: Handle onNetworkSuspended();
@@ -411,7 +407,7 @@ public class UpstreamNetworkMonitor {
@Override
public void onLost(Network network) {
handleLost(mCallbackType, network);
recomputeOffloadExemptPrefixes();
recomputeLocalPrefixes();
}
}
@@ -460,35 +456,15 @@ public class UpstreamNetworkMonitor {
return result;
}
private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) {
private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) {
final HashSet<IpPrefix> prefixSet = new HashSet<>();
addDefaultLocalPrefixes(prefixSet);
for (NetworkState ns : netStates) {
addOffloadExemptPrefixes(prefixSet, ns.linkProperties);
final LinkProperties lp = ns.linkProperties;
if (lp == null) continue;
prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
}
return prefixSet;
}
private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) {
Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET);
}
private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) {
if (lp == null) return;
for (LinkAddress linkAddr : lp.getAllLinkAddresses()) {
prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength()));
}
// TODO: Consider adding other non-default routes associated with this
// network. Traffic to these destinations should perhaps not go through
// the Internet (upstream).
}
private static IpPrefix prefix(String prefixStr) {
return new IpPrefix(prefixStr);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2017 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.util;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @hide
*/
public class PrefixUtils {
private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = {
pfx("127.0.0.0/8"), // IPv4 loopback
pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8
pfx("::/3"),
pfx("fe80::/64"), // IPv6 link-local
pfx("fc00::/7"), // IPv6 ULA
pfx("ff02::/8"), // IPv6 link-local multicast
};
public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24");
public static Set<IpPrefix> getNonForwardablePrefixes() {
final HashSet<IpPrefix> prefixes = new HashSet<>();
addNonForwardablePrefixes(prefixes);
return prefixes;
}
public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) {
Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES);
}
public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) {
final HashSet<IpPrefix> localPrefixes = new HashSet<>();
if (lp == null) return localPrefixes;
for (LinkAddress addr : lp.getAllLinkAddresses()) {
if (addr.getAddress().isLinkLocalAddress()) continue;
localPrefixes.add(asIpPrefix(addr));
}
// TODO: Add directly-connected routes as well (ones from which we did
// not also form a LinkAddress)?
return localPrefixes;
}
public static IpPrefix asIpPrefix(LinkAddress addr) {
return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
}
private static IpPrefix pfx(String prefixStr) {
return new IpPrefix(prefixStr);
}
}

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.RouteInfo;
@@ -45,6 +46,8 @@ import com.android.internal.util.test.FakeSettingsProvider;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
@@ -186,17 +189,43 @@ public class OffloadControllerTest {
any(OffloadHardwareInterface.ControlCallback.class));
inOrder.verifyNoMoreInteractions();
offload.setUpstreamLinkProperties(null);
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(null), eq(null), eq(null), eq(null));
// In reality, the UpstreamNetworkMonitor would have passed down to us
// a covering set of local prefixes representing a minimum essential
// set plus all the prefixes on networks with network agents.
//
// We simulate that there, and then add upstream elements one by one
// and watch what happens.
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);
inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
assertEquals(4, localPrefixes.size());
assertTrue(localPrefixes.contains("127.0.0.0/8"));
assertTrue(localPrefixes.contains("192.0.2.0/24"));
assertTrue(localPrefixes.contains("fe80::/64"));
assertTrue(localPrefixes.contains("2001:db8::/64"));
inOrder.verifyNoMoreInteractions();
offload.setUpstreamLinkProperties(null);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
// This LinkProperties value does not differ from the default upstream.
// There should be no extraneous call to setUpstreamParameters().
inOrder.verify(mHardware, never()).setUpstreamParameters(
anyObject(), anyObject(), anyObject(), anyObject());
inOrder.verifyNoMoreInteractions();
reset(mHardware);
final LinkProperties lp = new LinkProperties();
final String testIfName = "rmnet_data17";
lp.setInterfaceName(testIfName);
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(null), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
@@ -204,7 +233,15 @@ public class OffloadControllerTest {
final String ipv4Addr = "192.0.2.5";
final String linkAddr = ipv4Addr + "/24";
lp.addLinkAddress(new LinkAddress(linkAddr));
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24")));
offload.setUpstreamLinkProperties(lp);
// IPv4 prefixes and addresses on the upstream are simply left as whole
// prefixes (already passed in from UpstreamNetworkMonitor code). If a
// tethering client sends traffic to the IPv4 default router or other
// clients on the upstream this will not be hardware-forwarded, and that
// should be fine for now. Ergo: no change in local addresses, no call
// to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
@@ -212,6 +249,8 @@ public class OffloadControllerTest {
final String ipv4Gateway = "192.0.2.1";
lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
inOrder.verifyNoMoreInteractions();
@@ -219,6 +258,8 @@ public class OffloadControllerTest {
final String ipv6Gw1 = "fe80::cafe";
lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1)));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
ArrayList<String> v6gws = mStringArrayCaptor.getValue();
@@ -229,6 +270,8 @@ public class OffloadControllerTest {
final String ipv6Gw2 = "fe80::d00d";
lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2)));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
v6gws = mStringArrayCaptor.getValue();
@@ -244,6 +287,8 @@ public class OffloadControllerTest {
stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00")));
assertTrue(lp.addStackedLink(stacked));
offload.setUpstreamLinkProperties(lp);
// No change in local addresses means no call to setLocalPrefixes().
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
v6gws = mStringArrayCaptor.getValue();
@@ -251,5 +296,43 @@ public class OffloadControllerTest {
assertTrue(v6gws.contains(ipv6Gw1));
assertTrue(v6gws.contains(ipv6Gw2));
inOrder.verifyNoMoreInteractions();
// Add in some IPv6 upstream info. When there is a tethered downstream
// making use of the IPv6 prefix we would expect to see the /64 route
// removed from "local prefixes" and /128s added for the upstream IPv6
// addresses. This is not yet implemented, and for now we simply
// expect to see these /128s.
lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64")));
// "2001:db8::/64" plus "assigned" ASCII in hex
lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64"));
// "2001:db8::/64" plus "random" ASCII in hex
lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64"));
offload.setUpstreamLinkProperties(lp);
inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
localPrefixes = mStringArrayCaptor.getValue();
assertEquals(6, localPrefixes.size());
assertTrue(localPrefixes.contains("127.0.0.0/8"));
assertTrue(localPrefixes.contains("192.0.2.0/24"));
assertTrue(localPrefixes.contains("fe80::/64"));
assertTrue(localPrefixes.contains("2001:db8::/64"));
assertTrue(localPrefixes.contains("2001:db8::6173:7369:676e:6564/128"));
assertTrue(localPrefixes.contains("2001:db8::7261:6e64:6f6d/128"));
// The relevant parts of the LinkProperties have not changed, but at the
// moment we do not de-dup upstream LinkProperties this carefully.
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();
// Completely identical LinkProperties updates are de-duped.
offload.setUpstreamLinkProperties(lp);
// This LinkProperties value does not differ from the default upstream.
// There should be no extraneous call to setUpstreamParameters().
inOrder.verify(mHardware, never()).setUpstreamParameters(
anyObject(), anyObject(), anyObject(), anyObject());
inOrder.verifyNoMoreInteractions();
}
}

View File

@@ -324,19 +324,14 @@ public class UpstreamNetworkMonitorTest {
}
@Test
public void testOffloadExemptPrefixes() throws Exception {
public void testLocalPrefixes() throws Exception {
mUNM.start();
// [0] Test minimum set of exempt prefixes.
Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes();
final String[] MINSET = {
"127.0.0.0/8", "169.254.0.0/16",
"::/3", "fe80::/64", "fc00::/7", "ff00::/8",
};
assertPrefixSet(exempt, INCLUDES, MINSET);
// [0] Test minimum set of local prefixes.
Set<IpPrefix> local = mUNM.getLocalPrefixes();
assertTrue(local.isEmpty());
final Set<String> alreadySeen = new HashSet<>();
Collections.addAll(alreadySeen, MINSET);
assertEquals(alreadySeen.size(), exempt.size());
// [1] Pretend Wi-Fi connects.
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
@@ -355,15 +350,15 @@ public class UpstreamNetworkMonitorTest {
wifiAgent.fakeConnect();
wifiAgent.sendLinkProperties(wifiLp);
exempt = mUNM.getOffloadExemptPrefixes();
assertPrefixSet(exempt, INCLUDES, alreadySeen);
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
final String[] wifiLinkPrefixes = {
// Excludes link-local as that's already tested within MINSET.
// Link-local prefixes are excluded and dealt with elsewhere.
"100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
};
assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes);
assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
Collections.addAll(alreadySeen, wifiLinkPrefixes);
assertEquals(alreadySeen.size(), exempt.size());
assertEquals(alreadySeen.size(), local.size());
// [2] Pretend mobile connects.
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
@@ -379,12 +374,12 @@ public class UpstreamNetworkMonitorTest {
cellAgent.fakeConnect();
cellAgent.sendLinkProperties(cellLp);
exempt = mUNM.getOffloadExemptPrefixes();
assertPrefixSet(exempt, INCLUDES, alreadySeen);
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
Collections.addAll(alreadySeen, cellLinkPrefixes);
assertEquals(alreadySeen.size(), exempt.size());
assertEquals(alreadySeen.size(), local.size());
// [3] Pretend DUN connects.
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
@@ -401,21 +396,20 @@ public class UpstreamNetworkMonitorTest {
dunAgent.fakeConnect();
dunAgent.sendLinkProperties(dunLp);
exempt = mUNM.getOffloadExemptPrefixes();
assertPrefixSet(exempt, INCLUDES, alreadySeen);
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
Collections.addAll(alreadySeen, dunLinkPrefixes);
assertEquals(alreadySeen.size(), exempt.size());
assertEquals(alreadySeen.size(), local.size());
// [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no
// longer be included (should be properly removed).
wifiAgent.fakeDisconnect();
exempt = mUNM.getOffloadExemptPrefixes();
assertPrefixSet(exempt, INCLUDES, MINSET);
assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes);
assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
}
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {