Merge "Pass all offload-exempt prefixes into OffloadController"
This commit is contained in:
@@ -47,6 +47,7 @@ import android.hardware.usb.UsbManager;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.INetworkPolicyManager;
|
import android.net.INetworkPolicyManager;
|
||||||
import android.net.INetworkStatsService;
|
import android.net.INetworkStatsService;
|
||||||
|
import android.net.IpPrefix;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
@@ -104,6 +105,7 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
|
||||||
@@ -210,7 +212,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
mContext.getContentResolver(),
|
mContext.getContentResolver(),
|
||||||
mLog);
|
mLog);
|
||||||
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
|
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
|
||||||
mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog);
|
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK );
|
||||||
mForwardedDownstreams = new HashSet<>();
|
mForwardedDownstreams = new HashSet<>();
|
||||||
mSimChange = new SimChangeListener(
|
mSimChange = new SimChangeListener(
|
||||||
mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
|
mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
|
||||||
@@ -1066,7 +1068,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
// Needed because the canonical source of upstream truth is just the
|
// Needed because the canonical source of upstream truth is just the
|
||||||
// upstream interface name, |mCurrentUpstreamIface|. This is ripe for
|
// upstream interface name, |mCurrentUpstreamIface|. This is ripe for
|
||||||
// future simplification, once the upstream Network is canonical.
|
// future simplification, once the upstream Network is canonical.
|
||||||
boolean pertainsToCurrentUpstream(NetworkState ns) {
|
private boolean pertainsToCurrentUpstream(NetworkState ns) {
|
||||||
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
|
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
|
||||||
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
|
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
|
||||||
if (mCurrentUpstreamIface.equals(ifname)) {
|
if (mCurrentUpstreamIface.equals(ifname)) {
|
||||||
@@ -1100,6 +1102,12 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startOffloadController() {
|
||||||
|
mOffloadController.start();
|
||||||
|
mOffloadController.updateExemptPrefixes(
|
||||||
|
mUpstreamNetworkMonitor.getOffloadExemptPrefixes());
|
||||||
|
}
|
||||||
|
|
||||||
class TetherMasterSM extends StateMachine {
|
class TetherMasterSM extends StateMachine {
|
||||||
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
|
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
|
||||||
// an interface SM has requested Tethering/Local Hotspot
|
// an interface SM has requested Tethering/Local Hotspot
|
||||||
@@ -1193,146 +1201,138 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TetherMasterUtilState extends State {
|
protected boolean turnOnMasterTetherSettings() {
|
||||||
@Override
|
final TetheringConfiguration cfg = mConfig;
|
||||||
public boolean processMessage(Message m) {
|
try {
|
||||||
|
mNMService.setIpForwardingEnabled(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
mLog.e(e);
|
||||||
|
transitionTo(mSetIpForwardingEnabledErrorState);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
|
||||||
protected boolean turnOnMasterTetherSettings() {
|
try {
|
||||||
final TetheringConfiguration cfg = mConfig;
|
// TODO: Find a more accurate method name (startDHCPv4()?).
|
||||||
try {
|
mNMService.startTethering(cfg.dhcpRanges);
|
||||||
mNMService.setIpForwardingEnabled(true);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
|
||||||
mLog.e(e);
|
|
||||||
transitionTo(mSetIpForwardingEnabledErrorState);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
|
|
||||||
try {
|
|
||||||
// TODO: Find a more accurate method name (startDHCPv4()?).
|
|
||||||
mNMService.startTethering(cfg.dhcpRanges);
|
|
||||||
} catch (Exception e) {
|
|
||||||
try {
|
|
||||||
mNMService.stopTethering();
|
|
||||||
mNMService.startTethering(cfg.dhcpRanges);
|
|
||||||
} catch (Exception ee) {
|
|
||||||
mLog.e(ee);
|
|
||||||
transitionTo(mStartTetheringErrorState);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mLog.log("SET master tether settings: ON");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean turnOffMasterTetherSettings() {
|
|
||||||
try {
|
try {
|
||||||
mNMService.stopTethering();
|
mNMService.stopTethering();
|
||||||
} catch (Exception e) {
|
mNMService.startTethering(cfg.dhcpRanges);
|
||||||
mLog.e(e);
|
} catch (Exception ee) {
|
||||||
transitionTo(mStopTetheringErrorState);
|
mLog.e(ee);
|
||||||
|
transitionTo(mStartTetheringErrorState);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
mNMService.setIpForwardingEnabled(false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
mLog.e(e);
|
|
||||||
transitionTo(mSetIpForwardingDisabledErrorState);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
transitionTo(mInitialState);
|
|
||||||
mLog.log("SET master tether settings: OFF");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
mLog.log("SET master tether settings: ON");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected void chooseUpstreamType(boolean tryCell) {
|
protected boolean turnOffMasterTetherSettings() {
|
||||||
updateConfiguration(); // TODO - remove?
|
try {
|
||||||
|
mNMService.stopTethering();
|
||||||
final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
|
} catch (Exception e) {
|
||||||
mConfig.preferredUpstreamIfaceTypes);
|
mLog.e(e);
|
||||||
if (ns == null) {
|
transitionTo(mStopTetheringErrorState);
|
||||||
if (tryCell) {
|
return false;
|
||||||
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
|
|
||||||
// We think mobile should be coming up; don't set a retry.
|
|
||||||
} else {
|
|
||||||
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setUpstreamNetwork(ns);
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
mNMService.setIpForwardingEnabled(false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
mLog.e(e);
|
||||||
|
transitionTo(mSetIpForwardingDisabledErrorState);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
transitionTo(mInitialState);
|
||||||
|
mLog.log("SET master tether settings: OFF");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setUpstreamNetwork(NetworkState ns) {
|
protected void chooseUpstreamType(boolean tryCell) {
|
||||||
String iface = null;
|
updateConfiguration(); // TODO - remove?
|
||||||
if (ns != null && ns.linkProperties != null) {
|
|
||||||
// Find the interface with the default IPv4 route. It may be the
|
|
||||||
// interface described by linkProperties, or one of the interfaces
|
|
||||||
// stacked on top of it.
|
|
||||||
Log.i(TAG, "Finding IPv4 upstream interface on: " + ns.linkProperties);
|
|
||||||
RouteInfo ipv4Default = RouteInfo.selectBestRoute(
|
|
||||||
ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
|
|
||||||
if (ipv4Default != null) {
|
|
||||||
iface = ipv4Default.getInterface();
|
|
||||||
Log.i(TAG, "Found interface " + ipv4Default.getInterface());
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "No IPv4 upstream interface, giving up.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iface != null) {
|
final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
|
||||||
setDnsForwarders(ns.network, ns.linkProperties);
|
mConfig.preferredUpstreamIfaceTypes);
|
||||||
|
if (ns == null) {
|
||||||
|
if (tryCell) {
|
||||||
|
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
|
||||||
|
// We think mobile should be coming up; don't set a retry.
|
||||||
|
} else {
|
||||||
|
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
|
||||||
}
|
}
|
||||||
notifyTetheredOfNewUpstreamIface(iface);
|
}
|
||||||
if (ns != null && pertainsToCurrentUpstream(ns)) {
|
setUpstreamNetwork(ns);
|
||||||
// If we already have NetworkState for this network examine
|
}
|
||||||
// it immediately, because there likely will be no second
|
|
||||||
// EVENT_ON_AVAILABLE (it was already received).
|
protected void setUpstreamNetwork(NetworkState ns) {
|
||||||
handleNewUpstreamNetworkState(ns);
|
String iface = null;
|
||||||
} else if (mCurrentUpstreamIface == null) {
|
if (ns != null && ns.linkProperties != null) {
|
||||||
// There are no available upstream networks, or none that
|
// Find the interface with the default IPv4 route. It may be the
|
||||||
// have an IPv4 default route (current metric for success).
|
// interface described by linkProperties, or one of the interfaces
|
||||||
handleNewUpstreamNetworkState(null);
|
// stacked on top of it.
|
||||||
|
Log.i(TAG, "Finding IPv4 upstream interface on: " + ns.linkProperties);
|
||||||
|
RouteInfo ipv4Default = RouteInfo.selectBestRoute(
|
||||||
|
ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
|
||||||
|
if (ipv4Default != null) {
|
||||||
|
iface = ipv4Default.getInterface();
|
||||||
|
Log.i(TAG, "Found interface " + ipv4Default.getInterface());
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "No IPv4 upstream interface, giving up.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
|
if (iface != null) {
|
||||||
// TODO: Set v4 and/or v6 DNS per available connectivity.
|
setDnsForwarders(ns.network, ns.linkProperties);
|
||||||
String[] dnsServers = mConfig.defaultIPv4DNS;
|
|
||||||
final Collection<InetAddress> dnses = lp.getDnsServers();
|
|
||||||
// TODO: Properly support the absence of DNS servers.
|
|
||||||
if (dnses != null && !dnses.isEmpty()) {
|
|
||||||
// TODO: remove this invocation of NetworkUtils.makeStrings().
|
|
||||||
dnsServers = NetworkUtils.makeStrings(dnses);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mNMService.setDnsForwarders(network, dnsServers);
|
|
||||||
mLog.log(String.format(
|
|
||||||
"SET DNS forwarders: network=%s dnsServers=%s",
|
|
||||||
network, Arrays.toString(dnsServers)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO: Investigate how this can fail and what exactly
|
|
||||||
// happens if/when such failures occur.
|
|
||||||
mLog.e("setting DNS forwarders failed, " + e);
|
|
||||||
transitionTo(mSetDnsForwardersErrorState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
notifyTetheredOfNewUpstreamIface(iface);
|
||||||
|
if (ns != null && pertainsToCurrentUpstream(ns)) {
|
||||||
|
// If we already have NetworkState for this network examine
|
||||||
|
// it immediately, because there likely will be no second
|
||||||
|
// EVENT_ON_AVAILABLE (it was already received).
|
||||||
|
handleNewUpstreamNetworkState(ns);
|
||||||
|
} else if (mCurrentUpstreamIface == null) {
|
||||||
|
// There are no available upstream networks, or none that
|
||||||
|
// have an IPv4 default route (current metric for success).
|
||||||
|
handleNewUpstreamNetworkState(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
|
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
|
||||||
if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
|
// TODO: Set v4 and/or v6 DNS per available connectivity.
|
||||||
mCurrentUpstreamIface = ifaceName;
|
String[] dnsServers = mConfig.defaultIPv4DNS;
|
||||||
for (TetherInterfaceStateMachine sm : mNotifyList) {
|
final Collection<InetAddress> dnses = lp.getDnsServers();
|
||||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
// TODO: Properly support the absence of DNS servers.
|
||||||
ifaceName);
|
if (dnses != null && !dnses.isEmpty()) {
|
||||||
}
|
// TODO: remove this invocation of NetworkUtils.makeStrings().
|
||||||
|
dnsServers = NetworkUtils.makeStrings(dnses);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
mNMService.setDnsForwarders(network, dnsServers);
|
||||||
|
mLog.log(String.format(
|
||||||
|
"SET DNS forwarders: network=%s dnsServers=%s",
|
||||||
|
network, Arrays.toString(dnsServers)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: Investigate how this can fail and what exactly
|
||||||
|
// happens if/when such failures occur.
|
||||||
|
mLog.e("setting DNS forwarders failed, " + e);
|
||||||
|
transitionTo(mSetDnsForwardersErrorState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void handleNewUpstreamNetworkState(NetworkState ns) {
|
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
|
||||||
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
|
if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
|
||||||
mOffloadController.setUpstreamLinkProperties(
|
mCurrentUpstreamIface = ifaceName;
|
||||||
(ns != null) ? ns.linkProperties : null);
|
for (TetherInterfaceStateMachine sm : mNotifyList) {
|
||||||
|
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
||||||
|
ifaceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void handleNewUpstreamNetworkState(NetworkState ns) {
|
||||||
|
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
|
||||||
|
mOffloadController.setUpstreamLinkProperties((ns != null) ? ns.linkProperties : null);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
|
private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
|
||||||
if (mNotifyList.indexOf(who) < 0) {
|
if (mNotifyList.indexOf(who) < 0) {
|
||||||
mNotifyList.add(who);
|
mNotifyList.add(who);
|
||||||
@@ -1379,7 +1379,61 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TetherModeAliveState extends TetherMasterUtilState {
|
private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
|
||||||
|
if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) {
|
||||||
|
mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final NetworkState ns = (NetworkState) o;
|
||||||
|
|
||||||
|
if (ns == null || !pertainsToCurrentUpstream(ns)) {
|
||||||
|
// TODO: In future, this is where upstream evaluation and selection
|
||||||
|
// could be handled for notifications which include sufficient data.
|
||||||
|
// For example, after CONNECTIVITY_ACTION listening is removed, here
|
||||||
|
// is where we could observe a Wi-Fi network becoming available and
|
||||||
|
// passing validation.
|
||||||
|
if (mCurrentUpstreamIface == null) {
|
||||||
|
// If we have no upstream interface, try to run through upstream
|
||||||
|
// selection again. If, for example, IPv4 connectivity has shown up
|
||||||
|
// after IPv6 (e.g., 464xlat became available) we want the chance to
|
||||||
|
// notice and act accordingly.
|
||||||
|
chooseUpstreamType(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (arg1) {
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
|
||||||
|
// The default network changed, or DUN connected
|
||||||
|
// before this callback was processed. Updates
|
||||||
|
// for the current NetworkCapabilities and
|
||||||
|
// LinkProperties have been requested (default
|
||||||
|
// request) or are being sent shortly (DUN). Do
|
||||||
|
// nothing until they arrive; if no updates
|
||||||
|
// arrive there's nothing to do.
|
||||||
|
break;
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
|
||||||
|
handleNewUpstreamNetworkState(ns);
|
||||||
|
break;
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
|
||||||
|
setDnsForwarders(ns.network, ns.linkProperties);
|
||||||
|
handleNewUpstreamNetworkState(ns);
|
||||||
|
break;
|
||||||
|
case UpstreamNetworkMonitor.EVENT_ON_LOST:
|
||||||
|
// TODO: Re-evaluate possible upstreams. Currently upstream
|
||||||
|
// reevaluation is triggered via received CONNECTIVITY_ACTION
|
||||||
|
// broadcasts that result in being passed a
|
||||||
|
// TetherMasterSM.CMD_UPSTREAM_CHANGED.
|
||||||
|
handleNewUpstreamNetworkState(null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mLog.e("Unknown arg1 value: " + arg1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TetherModeAliveState extends State {
|
||||||
boolean mUpstreamWanted = false;
|
boolean mUpstreamWanted = false;
|
||||||
boolean mTryCell = true;
|
boolean mTryCell = true;
|
||||||
|
|
||||||
@@ -1397,7 +1451,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
// TODO: De-duplicate with updateUpstreamWanted() below.
|
// TODO: De-duplicate with updateUpstreamWanted() below.
|
||||||
if (upstreamWanted()) {
|
if (upstreamWanted()) {
|
||||||
mUpstreamWanted = true;
|
mUpstreamWanted = true;
|
||||||
mOffloadController.start();
|
startOffloadController();
|
||||||
chooseUpstreamType(true);
|
chooseUpstreamType(true);
|
||||||
mTryCell = false;
|
mTryCell = false;
|
||||||
}
|
}
|
||||||
@@ -1417,7 +1471,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
mUpstreamWanted = upstreamWanted();
|
mUpstreamWanted = upstreamWanted();
|
||||||
if (mUpstreamWanted != previousUpstreamWanted) {
|
if (mUpstreamWanted != previousUpstreamWanted) {
|
||||||
if (mUpstreamWanted) {
|
if (mUpstreamWanted) {
|
||||||
mOffloadController.start();
|
startOffloadController();
|
||||||
} else {
|
} else {
|
||||||
mOffloadController.stop();
|
mOffloadController.stop();
|
||||||
}
|
}
|
||||||
@@ -1497,52 +1551,8 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
break;
|
break;
|
||||||
case EVENT_UPSTREAM_CALLBACK: {
|
case EVENT_UPSTREAM_CALLBACK: {
|
||||||
updateUpstreamWanted();
|
updateUpstreamWanted();
|
||||||
if (!mUpstreamWanted) break;
|
if (mUpstreamWanted) {
|
||||||
|
handleUpstreamNetworkMonitorCallback(message.arg1, message.obj);
|
||||||
final NetworkState ns = (NetworkState) message.obj;
|
|
||||||
|
|
||||||
if (ns == null || !pertainsToCurrentUpstream(ns)) {
|
|
||||||
// TODO: In future, this is where upstream evaluation and selection
|
|
||||||
// could be handled for notifications which include sufficient data.
|
|
||||||
// For example, after CONNECTIVITY_ACTION listening is removed, here
|
|
||||||
// is where we could observe a Wi-Fi network becoming available and
|
|
||||||
// passing validation.
|
|
||||||
if (mCurrentUpstreamIface == null) {
|
|
||||||
// If we have no upstream interface, try to run through upstream
|
|
||||||
// selection again. If, for example, IPv4 connectivity has shown up
|
|
||||||
// after IPv6 (e.g., 464xlat became available) we want the chance to
|
|
||||||
// notice and act accordingly.
|
|
||||||
chooseUpstreamType(false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (message.arg1) {
|
|
||||||
case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
|
|
||||||
// The default network changed, or DUN connected
|
|
||||||
// before this callback was processed. Updates
|
|
||||||
// for the current NetworkCapabilities and
|
|
||||||
// LinkProperties have been requested (default
|
|
||||||
// request) or are being sent shortly (DUN). Do
|
|
||||||
// nothing until they arrive; if no updates
|
|
||||||
// arrive there's nothing to do.
|
|
||||||
break;
|
|
||||||
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
|
|
||||||
handleNewUpstreamNetworkState(ns);
|
|
||||||
break;
|
|
||||||
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
|
|
||||||
setDnsForwarders(ns.network, ns.linkProperties);
|
|
||||||
handleNewUpstreamNetworkState(ns);
|
|
||||||
break;
|
|
||||||
case UpstreamNetworkMonitor.EVENT_ON_LOST:
|
|
||||||
// TODO: Re-evaluate possible upstreams. Currently upstream
|
|
||||||
// reevaluation is triggered via received CONNECTIVITY_ACTION
|
|
||||||
// broadcasts that result in being passed a
|
|
||||||
// TetherMasterSM.CMD_UPSTREAM_CHANGED.
|
|
||||||
handleNewUpstreamNetworkState(null);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering;
|
|||||||
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
|
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
import android.net.IpPrefix;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
@@ -28,6 +29,7 @@ import android.provider.Settings;
|
|||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to encapsulate the business logic of programming the tethering
|
* A class to encapsulate the business logic of programming the tethering
|
||||||
@@ -45,6 +47,7 @@ public class OffloadController {
|
|||||||
private boolean mConfigInitialized;
|
private boolean mConfigInitialized;
|
||||||
private boolean mControlInitialized;
|
private boolean mControlInitialized;
|
||||||
private LinkProperties mUpstreamLinkProperties;
|
private LinkProperties mUpstreamLinkProperties;
|
||||||
|
private Set<IpPrefix> mExemptPrefixes;
|
||||||
|
|
||||||
public OffloadController(Handler h, OffloadHardwareInterface hwi,
|
public OffloadController(Handler h, OffloadHardwareInterface hwi,
|
||||||
ContentResolver contentResolver, SharedLog log) {
|
ContentResolver contentResolver, SharedLog log) {
|
||||||
@@ -108,6 +111,17 @@ public class OffloadController {
|
|||||||
pushUpstreamParameters();
|
pushUpstreamParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
public void notifyDownstreamLinkProperties(LinkProperties lp) {
|
public void notifyDownstreamLinkProperties(LinkProperties lp) {
|
||||||
if (!started()) return;
|
if (!started()) return;
|
||||||
|
|
||||||
|
|||||||
@@ -26,18 +26,24 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.ConnectivityManager.NetworkCallback;
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
|
import android.net.IpPrefix;
|
||||||
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
|
import android.net.util.NetworkConstants;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,10 +72,16 @@ public class UpstreamNetworkMonitor {
|
|||||||
private static final boolean DBG = false;
|
private static final boolean DBG = false;
|
||||||
private static final boolean VDBG = 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_AVAILABLE = 1;
|
||||||
public static final int EVENT_ON_CAPABILITIES = 2;
|
public static final int EVENT_ON_CAPABILITIES = 2;
|
||||||
public static final int EVENT_ON_LINKPROPERTIES = 3;
|
public static final int EVENT_ON_LINKPROPERTIES = 3;
|
||||||
public static final int EVENT_ON_LOST = 4;
|
public static final int EVENT_ON_LOST = 4;
|
||||||
|
public static final int NOTIFY_EXEMPT_PREFIXES = 10;
|
||||||
|
|
||||||
private static final int CALLBACK_LISTEN_ALL = 1;
|
private static final int CALLBACK_LISTEN_ALL = 1;
|
||||||
private static final int CALLBACK_TRACK_DEFAULT = 2;
|
private static final int CALLBACK_TRACK_DEFAULT = 2;
|
||||||
@@ -81,6 +93,7 @@ public class UpstreamNetworkMonitor {
|
|||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final int mWhat;
|
private final int mWhat;
|
||||||
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
|
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
|
||||||
|
private HashSet<IpPrefix> mOffloadExemptPrefixes;
|
||||||
private ConnectivityManager mCM;
|
private ConnectivityManager mCM;
|
||||||
private NetworkCallback mListenAllCallback;
|
private NetworkCallback mListenAllCallback;
|
||||||
private NetworkCallback mDefaultNetworkCallback;
|
private NetworkCallback mDefaultNetworkCallback;
|
||||||
@@ -88,18 +101,19 @@ public class UpstreamNetworkMonitor {
|
|||||||
private boolean mDunRequired;
|
private boolean mDunRequired;
|
||||||
private Network mCurrentDefault;
|
private Network mCurrentDefault;
|
||||||
|
|
||||||
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) {
|
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
|
||||||
mContext = ctx;
|
mContext = ctx;
|
||||||
mTarget = tgt;
|
mTarget = tgt;
|
||||||
mHandler = mTarget.getHandler();
|
mHandler = mTarget.getHandler();
|
||||||
mWhat = what;
|
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
|
mWhat = what;
|
||||||
|
mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public UpstreamNetworkMonitor(
|
public UpstreamNetworkMonitor(
|
||||||
StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) {
|
ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
|
||||||
this(null, tgt, what, log);
|
this((Context) null, tgt, log, what);
|
||||||
mCM = cm;
|
mCM = cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +223,10 @@ public class UpstreamNetworkMonitor {
|
|||||||
return typeStatePair.ns;
|
return typeStatePair.ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<IpPrefix> getOffloadExemptPrefixes() {
|
||||||
|
return (Set<IpPrefix>) mOffloadExemptPrefixes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleAvailable(int callbackType, Network network) {
|
private void handleAvailable(int callbackType, Network network) {
|
||||||
if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
|
if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
|
||||||
|
|
||||||
@@ -342,6 +360,14 @@ public class UpstreamNetworkMonitor {
|
|||||||
notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch (and cache) a ConnectivityManager only if and when we need one.
|
// Fetch (and cache) a ConnectivityManager only if and when we need one.
|
||||||
private ConnectivityManager cm() {
|
private ConnectivityManager cm() {
|
||||||
if (mCM == null) {
|
if (mCM == null) {
|
||||||
@@ -376,6 +402,7 @@ public class UpstreamNetworkMonitor {
|
|||||||
@Override
|
@Override
|
||||||
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
|
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
|
||||||
handleLinkProp(network, newLp);
|
handleLinkProp(network, newLp);
|
||||||
|
recomputeOffloadExemptPrefixes();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle onNetworkSuspended();
|
// TODO: Handle onNetworkSuspended();
|
||||||
@@ -384,6 +411,7 @@ public class UpstreamNetworkMonitor {
|
|||||||
@Override
|
@Override
|
||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
handleLost(mCallbackType, network);
|
handleLost(mCallbackType, network);
|
||||||
|
recomputeOffloadExemptPrefixes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,16 +423,16 @@ public class UpstreamNetworkMonitor {
|
|||||||
notifyTarget(which, mNetworkMap.get(network));
|
notifyTarget(which, mNetworkMap.get(network));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyTarget(int which, NetworkState netstate) {
|
private void notifyTarget(int which, Object obj) {
|
||||||
mTarget.sendMessage(mWhat, which, 0, netstate);
|
mTarget.sendMessage(mWhat, which, 0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static private class TypeStatePair {
|
private static class TypeStatePair {
|
||||||
public int type = TYPE_NONE;
|
public int type = TYPE_NONE;
|
||||||
public NetworkState ns = null;
|
public NetworkState ns = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static private TypeStatePair findFirstAvailableUpstreamByType(
|
private static TypeStatePair findFirstAvailableUpstreamByType(
|
||||||
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
|
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
|
||||||
final TypeStatePair result = new TypeStatePair();
|
final TypeStatePair result = new TypeStatePair();
|
||||||
|
|
||||||
@@ -431,4 +459,36 @@ public class UpstreamNetworkMonitor {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) {
|
||||||
|
final HashSet<IpPrefix> prefixSet = new HashSet<>();
|
||||||
|
|
||||||
|
addDefaultLocalPrefixes(prefixSet);
|
||||||
|
|
||||||
|
for (NetworkState ns : netStates) {
|
||||||
|
addOffloadExemptPrefixes(prefixSet, ns.linkProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ import android.os.Message;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.ConnectivityManager.NetworkCallback;
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.IConnectivityManager;
|
import android.net.IConnectivityManager;
|
||||||
|
import android.net.IpPrefix;
|
||||||
|
import android.net.LinkAddress;
|
||||||
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
@@ -66,6 +69,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -77,6 +81,9 @@ import java.util.Set;
|
|||||||
public class UpstreamNetworkMonitorTest {
|
public class UpstreamNetworkMonitorTest {
|
||||||
private static final int EVENT_UNM_UPDATE = 1;
|
private static final int EVENT_UNM_UPDATE = 1;
|
||||||
|
|
||||||
|
private static final boolean INCLUDES = true;
|
||||||
|
private static final boolean EXCLUDES = false;
|
||||||
|
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
@Mock private IConnectivityManager mCS;
|
@Mock private IConnectivityManager mCS;
|
||||||
@Mock private SharedLog mLog;
|
@Mock private SharedLog mLog;
|
||||||
@@ -94,7 +101,8 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
|
|
||||||
mCM = spy(new TestConnectivityManager(mContext, mCS));
|
mCM = spy(new TestConnectivityManager(mContext, mCS));
|
||||||
mSM = new TestStateMachine();
|
mSM = new TestStateMachine();
|
||||||
mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM, mLog);
|
mUNM = new UpstreamNetworkMonitor(
|
||||||
|
(ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After public void tearDown() throws Exception {
|
@After public void tearDown() throws Exception {
|
||||||
@@ -315,6 +323,101 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOffloadExemptPrefixes() 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);
|
||||||
|
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);
|
||||||
|
final LinkProperties wifiLp = new LinkProperties();
|
||||||
|
wifiLp.setInterfaceName("wlan0");
|
||||||
|
final String[] WIFI_ADDRS = {
|
||||||
|
"fe80::827a:bfff:fe6f:374d", "100.112.103.18",
|
||||||
|
"2001:db8:4:fd00:827a:bfff:fe6f:374d",
|
||||||
|
"2001:db8:4:fd00:6dea:325a:fdae:4ef4",
|
||||||
|
"fd6a:a640:60bf:e985::123", // ULA address for good measure.
|
||||||
|
};
|
||||||
|
for (String addrStr : WIFI_ADDRS) {
|
||||||
|
final String cidr = addrStr.contains(":") ? "/64" : "/20";
|
||||||
|
wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||||
|
}
|
||||||
|
wifiAgent.fakeConnect();
|
||||||
|
wifiAgent.sendLinkProperties(wifiLp);
|
||||||
|
|
||||||
|
exempt = mUNM.getOffloadExemptPrefixes();
|
||||||
|
assertPrefixSet(exempt, INCLUDES, alreadySeen);
|
||||||
|
final String[] wifiLinkPrefixes = {
|
||||||
|
// Excludes link-local as that's already tested within MINSET.
|
||||||
|
"100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
|
||||||
|
};
|
||||||
|
assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes);
|
||||||
|
Collections.addAll(alreadySeen, wifiLinkPrefixes);
|
||||||
|
assertEquals(alreadySeen.size(), exempt.size());
|
||||||
|
|
||||||
|
// [2] Pretend mobile connects.
|
||||||
|
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||||
|
final LinkProperties cellLp = new LinkProperties();
|
||||||
|
cellLp.setInterfaceName("rmnet_data0");
|
||||||
|
final String[] CELL_ADDRS = {
|
||||||
|
"10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
|
||||||
|
};
|
||||||
|
for (String addrStr : CELL_ADDRS) {
|
||||||
|
final String cidr = addrStr.contains(":") ? "/64" : "/27";
|
||||||
|
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||||
|
}
|
||||||
|
cellAgent.fakeConnect();
|
||||||
|
cellAgent.sendLinkProperties(cellLp);
|
||||||
|
|
||||||
|
exempt = mUNM.getOffloadExemptPrefixes();
|
||||||
|
assertPrefixSet(exempt, INCLUDES, alreadySeen);
|
||||||
|
final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
|
||||||
|
assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
|
||||||
|
Collections.addAll(alreadySeen, cellLinkPrefixes);
|
||||||
|
assertEquals(alreadySeen.size(), exempt.size());
|
||||||
|
|
||||||
|
// [3] Pretend DUN connects.
|
||||||
|
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||||
|
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
|
||||||
|
final LinkProperties dunLp = new LinkProperties();
|
||||||
|
dunLp.setInterfaceName("rmnet_data1");
|
||||||
|
final String[] DUN_ADDRS = {
|
||||||
|
"192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
|
||||||
|
};
|
||||||
|
for (String addrStr : DUN_ADDRS) {
|
||||||
|
final String cidr = addrStr.contains(":") ? "/64" : "/27";
|
||||||
|
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
|
||||||
|
}
|
||||||
|
dunAgent.fakeConnect();
|
||||||
|
dunAgent.sendLinkProperties(dunLp);
|
||||||
|
|
||||||
|
exempt = mUNM.getOffloadExemptPrefixes();
|
||||||
|
assertPrefixSet(exempt, INCLUDES, alreadySeen);
|
||||||
|
final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
|
||||||
|
assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
|
||||||
|
Collections.addAll(alreadySeen, dunLinkPrefixes);
|
||||||
|
assertEquals(alreadySeen.size(), exempt.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);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
|
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
|
||||||
if (legacyType == TYPE_NONE) {
|
if (legacyType == TYPE_NONE) {
|
||||||
assertTrue(ns == null);
|
assertTrue(ns == null);
|
||||||
@@ -476,6 +579,12 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
cb.onLost(networkId);
|
cb.onLost(networkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendLinkProperties(LinkProperties lp) {
|
||||||
|
for (NetworkCallback cb : cm.listening.keySet()) {
|
||||||
|
cb.onLinkPropertiesChanged(networkId, lp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestStateMachine extends StateMachine {
|
public static class TestStateMachine extends StateMachine {
|
||||||
@@ -504,4 +613,19 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
static NetworkCapabilities copy(NetworkCapabilities nc) {
|
static NetworkCapabilities copy(NetworkCapabilities nc) {
|
||||||
return new NetworkCapabilities(nc);
|
return new NetworkCapabilities(nc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
|
||||||
|
final Set<String> expectedSet = new HashSet<>();
|
||||||
|
Collections.addAll(expectedSet, expected);
|
||||||
|
assertPrefixSet(prefixes, expectation, expectedSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
|
||||||
|
for (String expectedPrefix : expected) {
|
||||||
|
final String errStr = expectation ? "did not find" : "found";
|
||||||
|
assertEquals(
|
||||||
|
String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
|
||||||
|
expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user