Merge "Initial rename to IpServer"

am: 31830456cd

Change-Id: Iac2c2fddae78fe642ebdc6e5acf12dffcb636d22
This commit is contained in:
Erik Kline
2018-09-13 05:09:38 -07:00
committed by android-build-merger
7 changed files with 321 additions and 375 deletions

View File

@@ -68,6 +68,7 @@ import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -112,10 +113,8 @@ import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
@@ -149,7 +148,7 @@ public class Tethering extends BaseNetworkObserver {
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
private static final Class[] messageClasses = {
Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
@@ -159,21 +158,21 @@ public class Tethering extends BaseNetworkObserver {
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
private static class TetherState {
public final TetherInterfaceStateMachine stateMachine;
public final IpServer ipServer;
public int lastState;
public int lastError;
public TetherState(TetherInterfaceStateMachine sm) {
stateMachine = sm;
public TetherState(IpServer ipServer) {
this.ipServer = ipServer;
// Assume all state machines start out available and with no errors.
lastState = IControlsTethering.STATE_AVAILABLE;
lastState = IpServer.STATE_AVAILABLE;
lastError = TETHER_ERROR_NO_ERROR;
}
public boolean isCurrentlyServing() {
switch (lastState) {
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_TETHERED:
case IpServer.STATE_LOCAL_ONLY:
return true;
default:
return false;
@@ -198,7 +197,7 @@ public class Tethering extends BaseNetworkObserver {
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
// TODO: Figure out how to merge this and other downstream-tracking objects
// into a single coherent structure.
private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
@@ -604,7 +603,7 @@ public class Tethering extends BaseNetworkObserver {
}
public int tether(String iface) {
return tether(iface, IControlsTethering.STATE_TETHERED);
return tether(iface, IpServer.STATE_TETHERED);
}
private int tether(String iface, int requestedState) {
@@ -617,7 +616,7 @@ public class Tethering extends BaseNetworkObserver {
}
// Ignore the error status of the interface. If the interface is available,
// the errors are referring to past tethering attempts anyway.
if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
@@ -626,8 +625,7 @@ public class Tethering extends BaseNetworkObserver {
// return an error.
//
// TODO: reexamine the threading and messaging model.
tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -644,8 +642,7 @@ public class Tethering extends BaseNetworkObserver {
Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE;
}
tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
return TETHER_ERROR_NO_ERROR;
}
}
@@ -689,11 +686,11 @@ public class Tethering extends BaseNetworkObserver {
String iface = mTetherStates.keyAt(i);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
} else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
} else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
availableList.add(iface);
} else if (tetherState.lastState == IControlsTethering.STATE_LOCAL_ONLY) {
} else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
localOnlyList.add(iface);
} else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
} else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
usbTethered = true;
} else if (cfg.isWifi(iface)) {
@@ -882,10 +879,10 @@ public class Tethering extends BaseNetworkObserver {
synchronized (Tethering.this.mPublicSync) {
if (!usbConnected && mRndisEnabled) {
// Turn off tethering if it was enabled and there is a disconnect.
tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
} else if (usbConfigured && rndisEnabled) {
// Tether if rndis is enabled and usb is configured.
tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
}
mRndisEnabled = usbConfigured && rndisEnabled;
}
@@ -959,15 +956,15 @@ public class Tethering extends BaseNetworkObserver {
if (!TextUtils.isEmpty(ifname)) {
final TetherState ts = mTetherStates.get(ifname);
if (ts != null) {
ts.stateMachine.unwanted();
ts.ipServer.unwanted();
return;
}
}
for (int i = 0; i < mTetherStates.size(); i++) {
TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
if (tism.interfaceType() == TETHERING_WIFI) {
tism.unwanted();
final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
if (ipServer.interfaceType() == TETHERING_WIFI) {
ipServer.unwanted();
return;
}
}
@@ -978,15 +975,15 @@ public class Tethering extends BaseNetworkObserver {
}
private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
// Map wifiIpMode values to IControlsTethering serving states, inferring
// Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
final int ipServingMode;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
ipServingMode = IControlsTethering.STATE_TETHERED;
ipServingMode = IpServer.STATE_TETHERED;
break;
case IFACE_IP_MODE_LOCAL_ONLY:
ipServingMode = IControlsTethering.STATE_LOCAL_ONLY;
ipServingMode = IpServer.STATE_LOCAL_ONLY;
break;
default:
mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
@@ -1041,12 +1038,12 @@ public class Tethering extends BaseNetworkObserver {
private void changeInterfaceState(String ifname, int requestedState) {
final int result;
switch (requestedState) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
case IpServer.STATE_UNAVAILABLE:
case IpServer.STATE_AVAILABLE:
result = untether(ifname);
break;
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_TETHERED:
case IpServer.STATE_LOCAL_ONLY:
result = tether(ifname, requestedState);
break;
default:
@@ -1104,7 +1101,7 @@ public class Tethering extends BaseNetworkObserver {
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
if (tetherState.lastState == IpServer.STATE_TETHERED) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1117,7 +1114,7 @@ public class Tethering extends BaseNetworkObserver {
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -1177,7 +1174,7 @@ public class Tethering extends BaseNetworkObserver {
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
if (tetherState.lastState != IpServer.STATE_TETHERED) {
continue; // Skip interfaces that aren't tethered.
}
String iface = mTetherStates.keyAt(i);
@@ -1231,7 +1228,7 @@ public class Tethering extends BaseNetworkObserver {
// Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
// so that the garbage collector does not clean up the state machine before it has a chance
// to tear itself down.
private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
private final ArrayList<IpServer> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
private final OffloadWrapper mOffload;
@@ -1268,17 +1265,19 @@ public class Tethering extends BaseNetworkObserver {
public boolean processMessage(Message message) {
logMessage(this, message.what);
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
final IpServer who = (IpServer) 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;
}
case EVENT_IFACE_SERVING_STATE_INACTIVE: {
final IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
break;
}
case EVENT_IFACE_UPDATE_LINKPROPERTIES:
// Silently ignore these for now.
break;
@@ -1410,8 +1409,8 @@ public class Tethering extends BaseNetworkObserver {
protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
mCurrentUpstreamIfaceSet = ifaces;
for (TetherInterfaceStateMachine sm : mNotifyList) {
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
for (IpServer ipServer : mNotifyList) {
ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces);
}
}
@@ -1420,13 +1419,13 @@ public class Tethering extends BaseNetworkObserver {
mOffload.updateUpstreamNetworkState(ns);
}
private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
private void handleInterfaceServingStateActive(int mode, IpServer who) {
if (mNotifyList.indexOf(who) < 0) {
mNotifyList.add(who);
mIPv6TetheringCoordinator.addActiveDownstream(who, mode);
}
if (mode == IControlsTethering.STATE_TETHERED) {
if (mode == IpServer.STATE_TETHERED) {
// No need to notify OffloadController just yet as there are no
// "offload-able" prefixes to pass along. This will handled
// when the TISM informs Tethering of its LinkProperties.
@@ -1441,10 +1440,10 @@ public class Tethering extends BaseNetworkObserver {
final WifiManager mgr = getWifiManager();
final String iface = who.interfaceName();
switch (mode) {
case IControlsTethering.STATE_TETHERED:
case IpServer.STATE_TETHERED:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED);
break;
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_LOCAL_ONLY:
mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY);
break;
default:
@@ -1454,7 +1453,7 @@ public class Tethering extends BaseNetworkObserver {
}
}
private void handleInterfaceServingStateInactive(TetherInterfaceStateMachine who) {
private void handleInterfaceServingStateInactive(IpServer who) {
mNotifyList.remove(who);
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
@@ -1563,10 +1562,10 @@ public class Tethering extends BaseNetworkObserver {
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
handleInterfaceServingStateActive(message.arg1, who);
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
mCurrentUpstreamIfaceSet);
// If there has been a change and an upstream is now
// desired, kick off the selection process.
@@ -1577,7 +1576,7 @@ public class Tethering extends BaseNetworkObserver {
break;
}
case EVENT_IFACE_SERVING_STATE_INACTIVE: {
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
IpServer who = (IpServer) message.obj;
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
handleInterfaceServingStateInactive(who);
@@ -1591,7 +1590,7 @@ public class Tethering extends BaseNetworkObserver {
if (DBG) {
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
" live requests:");
for (TetherInterfaceStateMachine o : mNotifyList) {
for (IpServer o : mNotifyList) {
Log.d(TAG, " " + o);
}
}
@@ -1605,7 +1604,7 @@ public class Tethering extends BaseNetworkObserver {
}
case EVENT_IFACE_UPDATE_LINKPROPERTIES: {
final LinkProperties newLp = (LinkProperties) message.obj;
if (message.arg1 == IControlsTethering.STATE_TETHERED) {
if (message.arg1 == IpServer.STATE_TETHERED) {
mOffload.updateDownstreamLinkProperties(newLp);
} else {
mOffload.excludeDownstreamInterface(newLp.getInterfaceName());
@@ -1650,7 +1649,7 @@ public class Tethering extends BaseNetworkObserver {
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
IpServer who = (IpServer) message.obj;
who.sendMessage(mErrorNotification);
break;
case CMD_CLEAR_ERROR:
@@ -1665,8 +1664,8 @@ public class Tethering extends BaseNetworkObserver {
void notify(int msgType) {
mErrorNotification = msgType;
for (TetherInterfaceStateMachine sm : mNotifyList) {
sm.sendMessage(msgType);
for (IpServer ipServer : mNotifyList) {
ipServer.sendMessage(msgType);
}
}
@@ -1676,7 +1675,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingEnabled");
notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR);
notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR);
}
}
@@ -1684,7 +1683,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingDisabled");
notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR);
notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR);
}
}
@@ -1692,7 +1691,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
Log.e(TAG, "Error in startTethering");
notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR);
notify(IpServer.CMD_START_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1703,7 +1702,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
Log.e(TAG, "Error in stopTethering");
notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR);
notify(IpServer.CMD_STOP_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
@@ -1714,7 +1713,7 @@ public class Tethering extends BaseNetworkObserver {
@Override
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR);
notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
try {
mNMService.stopTethering();
} catch (Exception e) {}
@@ -1771,15 +1770,15 @@ public class Tethering extends BaseNetworkObserver {
// Maybe add prefixes or addresses for downstreams, depending on
// the IP serving mode of each.
for (TetherInterfaceStateMachine tism : mNotifyList) {
final LinkProperties lp = tism.linkProperties();
for (IpServer ipServer : mNotifyList) {
final LinkProperties lp = ipServer.linkProperties();
switch (tism.servingMode()) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
switch (ipServer.servingMode()) {
case IpServer.STATE_UNAVAILABLE:
case IpServer.STATE_AVAILABLE:
// No usable LinkProperties in these states.
continue;
case IControlsTethering.STATE_TETHERED:
case IpServer.STATE_TETHERED:
// Only add IPv4 /32 and IPv6 /128 prefixes. The
// directly-connected prefixes will be sent as
// downstream "offload-able" prefixes.
@@ -1789,7 +1788,7 @@ public class Tethering extends BaseNetworkObserver {
localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip));
}
break;
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_LOCAL_ONLY:
// Add prefixes covering all local IPs.
localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp));
break;
@@ -1826,16 +1825,16 @@ public class Tethering extends BaseNetworkObserver {
pw.print(iface + " - ");
switch (tetherState.lastState) {
case IControlsTethering.STATE_UNAVAILABLE:
case IpServer.STATE_UNAVAILABLE:
pw.print("UnavailableState");
break;
case IControlsTethering.STATE_AVAILABLE:
case IpServer.STATE_AVAILABLE:
pw.print("AvailableState");
break;
case IControlsTethering.STATE_TETHERED:
case IpServer.STATE_TETHERED:
pw.print("TetheredState");
break;
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_LOCAL_ONLY:
pw.print("LocalHotspotState");
break;
default:
@@ -1873,28 +1872,26 @@ public class Tethering extends BaseNetworkObserver {
return false;
}
private IControlsTethering makeControlCallback(String ifname) {
return new IControlsTethering() {
private IpServer.Callback makeControlCallback() {
return new IpServer.Callback() {
@Override
public void updateInterfaceState(
TetherInterfaceStateMachine who, int state, int lastError) {
notifyInterfaceStateChange(ifname, who, state, lastError);
public void updateInterfaceState(IpServer who, int state, int lastError) {
notifyInterfaceStateChange(who, state, lastError);
}
@Override
public void updateLinkProperties(
TetherInterfaceStateMachine who, LinkProperties newLp) {
notifyLinkPropertiesChanged(ifname, who, newLp);
public void updateLinkProperties(IpServer who, LinkProperties newLp) {
notifyLinkPropertiesChanged(who, newLp);
}
};
}
// TODO: Move into TetherMasterSM.
private void notifyInterfaceStateChange(
String iface, TetherInterfaceStateMachine who, int state, int error) {
private void notifyInterfaceStateChange(IpServer who, int state, int error) {
final String iface = who.interfaceName();
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState != null && tetherState.stateMachine.equals(who)) {
if (tetherState != null && tetherState.ipServer.equals(who)) {
tetherState.lastState = state;
tetherState.lastError = error;
} else {
@@ -1908,7 +1905,7 @@ public class Tethering extends BaseNetworkObserver {
// Notify that we're tethering (or not) this interface.
// This is how data saver for instance knows if the user explicitly
// turned on tethering (thus keeping us from being in data saver mode).
mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED);
mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
} catch (RemoteException e) {
// Not really very much we can do here.
}
@@ -1921,12 +1918,12 @@ public class Tethering extends BaseNetworkObserver {
}
int which;
switch (state) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
case IpServer.STATE_UNAVAILABLE:
case IpServer.STATE_AVAILABLE:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
break;
case IControlsTethering.STATE_TETHERED:
case IControlsTethering.STATE_LOCAL_ONLY:
case IpServer.STATE_TETHERED:
case IpServer.STATE_LOCAL_ONLY:
which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
break;
default:
@@ -1937,12 +1934,12 @@ public class Tethering extends BaseNetworkObserver {
sendTetherStateChangedBroadcast();
}
private void notifyLinkPropertiesChanged(String iface, TetherInterfaceStateMachine who,
LinkProperties newLp) {
private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
final String iface = who.interfaceName();
final int state;
synchronized (mPublicSync) {
final TetherState tetherState = mTetherStates.get(iface);
if (tetherState != null && tetherState.stateMachine.equals(who)) {
if (tetherState != null && tetherState.ipServer.equals(who)) {
state = tetherState.lastState;
} else {
mLog.log("got notification from stale iface " + iface);
@@ -1952,7 +1949,7 @@ public class Tethering extends BaseNetworkObserver {
mLog.log(String.format(
"OBSERVED LinkProperties update iface=%s state=%s lp=%s",
iface, IControlsTethering.getStateString(state), newLp));
iface, IpServer.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
@@ -1976,11 +1973,11 @@ public class Tethering extends BaseNetworkObserver {
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
new TetherInterfaceStateMachine(
iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps));
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.stateMachine.start();
tetherState.ipServer.start();
}
private void stopTrackingInterfaceLocked(final String iface) {
@@ -1989,36 +1986,11 @@ public class Tethering extends BaseNetworkObserver {
mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
tetherState.stateMachine.stop();
tetherState.ipServer.stop();
mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
mTetherStates.remove(iface);
}
private static String getIPv4DefaultRouteInterface(NetworkState ns) {
if (ns == null) return null;
return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY);
}
private static String getIPv6DefaultRouteInterface(NetworkState ns) {
if (ns == null) return null;
// An upstream network's IPv6 capability is currently only useful if it
// can be 64share'd downstream (RFC 7278). For now, that means mobile
// upstream networks only.
if (ns.networkCapabilities == null ||
!ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
return null;
}
return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY);
}
private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
final RouteInfo ri = (lp != null)
? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
: null;
return (ri != null) ? ri.getInterface() : null;
}
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright (C) 2016 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.server.connectivity.tethering;
import android.net.LinkProperties;
/**
* @hide
*
* Interface with methods necessary to notify that a given interface is ready for tethering.
*
* Rename to something more representative, e.g. IpServingControlCallback.
*
* All methods MUST be called on the TetherMasterSM main Looper's thread.
*/
public class IControlsTethering {
public static final int STATE_UNAVAILABLE = 0;
public static final int STATE_AVAILABLE = 1;
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
public static String getStateString(int state) {
switch (state) {
case STATE_UNAVAILABLE: return "UNAVAILABLE";
case STATE_AVAILABLE: return "AVAILABLE";
case STATE_TETHERED: return "TETHERED";
case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
}
return "UNKNOWN: " + state;
}
/**
* Notify that |who| has changed its tethering state.
*
* TODO: Remove the need for the |who| argument.
*
* @param who corresponding instance of a TetherInterfaceStateMachine
* @param state one of IControlsTethering.STATE_*
* @param lastError one of ConnectivityManager.TETHER_ERROR_*
*/
public void updateInterfaceState(TetherInterfaceStateMachine who, int state, int lastError) {}
/**
* Notify that |who| has new LinkProperties.
*
* TODO: Remove the need for the |who| argument.
*
* @param who corresponding instance of a TetherInterfaceStateMachine
* @param newLp the new LinkProperties to report
*/
public void updateLinkProperties(TetherInterfaceStateMachine who, LinkProperties newLp) {}
}

View File

@@ -17,6 +17,7 @@
package com.android.server.connectivity.tethering;
import android.net.ConnectivityManager;
import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -50,19 +51,19 @@ public class IPv6TetheringCoordinator {
private static final boolean VDBG = false;
private static class Downstream {
public final TetherInterfaceStateMachine tism;
public final int mode; // IControlsTethering.STATE_*
public final IpServer ipServer;
public final int mode; // IpServer.STATE_*
// Used to append to a ULA /48, constructing a ULA /64 for local use.
public final short subnetId;
Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
this.tism = tism;
Downstream(IpServer ipServer, int mode, short subnetId) {
this.ipServer = ipServer;
this.mode = mode;
this.subnetId = subnetId;
}
}
private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
private final ArrayList<IpServer> mNotifyList;
private final SharedLog mLog;
// NOTE: mActiveDownstreams is a list and not a hash data structure because
// we keep active downstreams in arrival order. This is done so /64s can
@@ -74,8 +75,7 @@ public class IPv6TetheringCoordinator {
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
SharedLog log) {
public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
mLog = log.forSubComponent(TAG);
mActiveDownstreams = new LinkedList<>();
@@ -83,7 +83,7 @@ public class IPv6TetheringCoordinator {
mNextSubnetId = 0;
}
public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
public void addActiveDownstream(IpServer downstream, int mode) {
if (findDownstream(downstream) == null) {
// Adding a new downstream appends it to the list. Adding a
// downstream a second time without first removing it has no effect.
@@ -98,7 +98,7 @@ public class IPv6TetheringCoordinator {
}
}
public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
public void removeActiveDownstream(IpServer downstream) {
stopIPv6TetheringOn(downstream);
if (mActiveDownstreams.remove(findDownstream(downstream))) {
updateIPv6TetheringInterfaces();
@@ -133,8 +133,8 @@ public class IPv6TetheringCoordinator {
}
private void stopIPv6TetheringOnAllInterfaces() {
for (TetherInterfaceStateMachine sm : mNotifyList) {
stopIPv6TetheringOn(sm);
for (IpServer ipServer : mNotifyList) {
stopIPv6TetheringOn(ipServer);
}
}
@@ -156,28 +156,28 @@ public class IPv6TetheringCoordinator {
}
private void updateIPv6TetheringInterfaces() {
for (TetherInterfaceStateMachine sm : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
for (IpServer ipServer : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
// TODO: Figure out IPv6 support on PAN interfaces.
return null;
}
final Downstream ds = findDownstream(sm);
final Downstream ds = findDownstream(ipServer);
if (ds == null) return null;
if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
// Build a Unique Locally-assigned Prefix configuration.
return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
}
// This downstream is in IControlsTethering.STATE_TETHERED mode.
// This downstream is in IpServer.STATE_TETHERED mode.
if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
return null;
}
@@ -188,7 +188,7 @@ public class IPv6TetheringCoordinator {
// IPv6 toward the oldest (first requested) active downstream.
final Downstream currentActive = mActiveDownstreams.peek();
if (currentActive != null && currentActive.tism == sm) {
if (currentActive != null && currentActive.ipServer == ipServer) {
final LinkProperties lp = getIPv6OnlyLinkProperties(
mUpstreamNetworkState.linkProperties);
if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
@@ -199,9 +199,9 @@ public class IPv6TetheringCoordinator {
return null;
}
Downstream findDownstream(TetherInterfaceStateMachine tism) {
Downstream findDownstream(IpServer ipServer) {
for (Downstream ds : mActiveDownstreams) {
if (ds.tism == tism) return ds;
if (ds.ipServer == ipServer) return ds;
}
return null;
}
@@ -304,7 +304,7 @@ public class IPv6TetheringCoordinator {
ns.linkProperties);
}
private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
private static void stopIPv6TetheringOn(IpServer ipServer) {
ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
}
}

View File

@@ -21,6 +21,7 @@ import android.net.INetd;
import android.net.NetworkRequest;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
@@ -49,20 +50,12 @@ public class TetheringDependencies {
}
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
public InterfaceParams getInterfaceParams(String ifName) {
return InterfaceParams.getByName(ifName);
}
public INetd getNetdService() {
return NetdService.getInstance();
public IpServer.Dependencies getIpServerDependencies() {
return new IpServer.Dependencies();
}
public boolean isTetheringSupported() {
@@ -72,9 +65,4 @@ public class TetheringDependencies {
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params,
SharedLog log) {
return new DhcpServer(looper, iface, params, log);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
package android.net.ip;
import static android.net.NetworkUtils.numericToInetAddress;
import static android.net.util.NetworkConstants.asByte;
@@ -31,11 +31,10 @@ import android.net.LinkProperties;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.InterfaceController;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -67,7 +66,22 @@ import java.util.Set;
*
* @hide
*/
public class TetherInterfaceStateMachine extends StateMachine {
public class IpServer extends StateMachine {
public static final int STATE_UNAVAILABLE = 0;
public static final int STATE_AVAILABLE = 1;
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
public static String getStateString(int state) {
switch (state) {
case STATE_UNAVAILABLE: return "UNAVAILABLE";
case STATE_AVAILABLE: return "AVAILABLE";
case STATE_TETHERED: return "TETHERED";
case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
}
return "UNKNOWN: " + state;
}
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
private static final byte DOUG_ADAMS = (byte) 42;
@@ -83,15 +97,53 @@ public class TetherInterfaceStateMachine extends StateMachine {
// TODO: have this configurable
private static final int DHCP_LEASE_TIME_SECS = 3600;
private final static String TAG = "TetherInterfaceSM";
private final static String TAG = "IpServer";
private final static boolean DBG = false;
private final static boolean VDBG = false;
private static final Class[] messageClasses = {
TetherInterfaceStateMachine.class
IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
public static class Callback {
/**
* Notify that |who| has changed its tethering state.
*
* @param who the calling instance of IpServer
* @param state one of STATE_*
* @param lastError one of ConnectivityManager.TETHER_ERROR_*
*/
public void updateInterfaceState(IpServer who, int state, int lastError) {}
/**
* Notify that |who| has new LinkProperties.
*
* @param who the calling instance of IpServer
* @param newLp the new LinkProperties to report
*/
public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
}
public static class Dependencies {
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
public InterfaceParams getInterfaceParams(String ifName) {
return InterfaceParams.getByName(ifName);
}
public INetd getNetdService() {
return NetdService.getInstance();
}
public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
DhcpServingParams params, SharedLog log) {
return new DhcpServer(looper, iface, params, log);
}
}
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// request from the user that it wants to tether
public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
@@ -123,7 +175,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
private final INetworkManagementService mNMService;
private final INetd mNetd;
private final INetworkStatsService mStatsService;
private final IControlsTethering mTetherController;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final String mIfaceName;
@@ -131,7 +183,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
private final LinkProperties mLinkProperties;
private final boolean mUsingLegacyDhcp;
private final TetheringDependencies mDeps;
private final Dependencies mDeps;
private int mLastError;
private int mServingMode;
@@ -148,17 +200,16 @@ public class TetherInterfaceStateMachine extends StateMachine {
private DhcpServer mDhcpServer;
private RaParams mLastRaParams;
public TetherInterfaceStateMachine(
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetworkManagementService nMService, INetworkStatsService statsService,
IControlsTethering tetherController, boolean usingLegacyDhcp,
TetheringDependencies deps) {
Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
mNetd = deps.getNetdService();
mStatsService = statsService;
mTetherController = tetherController;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
@@ -167,7 +218,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
mDeps = deps;
resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mServingMode = IControlsTethering.STATE_AVAILABLE;
mServingMode = STATE_AVAILABLE;
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
@@ -521,14 +572,12 @@ public class TetherInterfaceStateMachine extends StateMachine {
private void sendInterfaceState(int newInterfaceState) {
mServingMode = newInterfaceState;
mTetherController.updateInterfaceState(
TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
sendLinkProperties();
}
private void sendLinkProperties() {
mTetherController.updateLinkProperties(
TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
}
private void resetLinkProperties() {
@@ -539,7 +588,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
class InitialState extends State {
@Override
public void enter() {
sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
sendInterfaceState(STATE_AVAILABLE);
}
@Override
@@ -549,10 +598,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
case CMD_TETHER_REQUESTED:
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
switch (message.arg1) {
case IControlsTethering.STATE_LOCAL_ONLY:
case STATE_LOCAL_ONLY:
transitionTo(mLocalHotspotState);
break;
case IControlsTethering.STATE_TETHERED:
case STATE_TETHERED:
transitionTo(mTetheredState);
break;
default:
@@ -649,7 +698,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of TetherInterfaceStateMachine.
// functional element outside of IpServer.
class LocalHotspotState extends BaseServingState {
@Override
public void enter() {
@@ -659,7 +708,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
}
if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
sendInterfaceState(STATE_LOCAL_ONLY);
}
@Override
@@ -685,7 +734,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
// problematic because transitioning during a multi-state jump yields
// a Log.wtf(). Ultimately, there should be only one ServingState,
// and forwarding and NAT rules should be handled by a coordinating
// functional element outside of TetherInterfaceStateMachine.
// functional element outside of IpServer.
class TetheredState extends BaseServingState {
@Override
public void enter() {
@@ -695,7 +744,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
}
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
sendInterfaceState(IControlsTethering.STATE_TETHERED);
sendInterfaceState(STATE_TETHERED);
}
@Override
@@ -817,7 +866,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
@Override
public void enter() {
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
sendInterfaceState(STATE_UNAVAILABLE);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
package android.net.ip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -37,9 +37,9 @@ import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
import static android.net.ip.IpServer.STATE_AVAILABLE;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -50,7 +50,6 @@ import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -74,7 +73,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetherInterfaceStateMachineTest {
public class IpServerTest {
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -85,39 +84,38 @@ public class TetherInterfaceStateMachineTest {
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
@Mock private IControlsTethering mTetherHelper;
@Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
@Mock private DhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private TetheringDependencies mTetheringDependencies;
@Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
private TetherInterfaceStateMachine mTestedSm;
private IpServer mIpServer;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */);
}
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
mTestedSm = new TetherInterfaceStateMachine(
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
mNMService, mStatsService, mTetherHelper, usingLegacyDhcp,
mTetheringDependencies);
mTestedSm.start();
mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
reset(mNMService, mStatsService, mTetherHelper);
reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
when(mTetheringDependencies.makeDhcpServer(
when(mDependencies.makeDhcpServer(
any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -130,11 +128,11 @@ public class TetherInterfaceStateMachineTest {
private void initTetheredStateMachine(int interfaceType, String upstreamIface,
boolean usingLegacyDhcp) throws Exception {
initStateMachine(interfaceType, usingLegacyDhcp);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
dispatchTetherConnectionChanged(upstreamIface);
}
reset(mNMService, mStatsService, mTetherHelper);
reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
}
@@ -145,34 +143,34 @@ public class TetherInterfaceStateMachineTest {
@Test
public void startsOutAvailable() {
mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
false /* usingLegacyDhcp */, mTetheringDependencies);
mTestedSm.start();
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
false /* usingLegacyDhcp */, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
}
@Test
public void shouldDoNothingUntilRequested() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
final int [] NOOP_COMMANDS = {
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
IpServer.CMD_TETHER_UNREQUESTED,
IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
IpServer.CMD_START_TETHERING_ERROR,
IpServer.CMD_STOP_TETHERING_ERROR,
IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
IpServer.CMD_TETHER_CONNECTION_CHANGED
};
for (int command : NOOP_COMMANDS) {
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -180,57 +178,57 @@ public class TetherInterfaceStateMachineTest {
public void handlesImmediateInterfaceDown() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
verify(mCallback).updateInterfaceState(
mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTethered() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mTetherHelper, mNMService);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canUnrequestTethering() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
public void canBeTetheredAsUsb() throws Exception {
initStateMachine(TETHERING_USB);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mTetherHelper, mNMService);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder inOrder = inOrder(mCallback, mNMService);
inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), mLinkPropertiesCaptor.capture());
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -243,7 +241,7 @@ public class TetherInterfaceStateMachineTest {
InOrder inOrder = inOrder(mNMService);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -257,7 +255,7 @@ public class TetherInterfaceStateMachineTest {
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -300,18 +298,18 @@ public class TetherInterfaceStateMachineTest {
public void canUnrequestTetheringWithUpstream() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
@Test
@@ -322,15 +320,15 @@ public class TetherInterfaceStateMachineTest {
if (shouldThrow) {
doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
}
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), mLinkPropertiesCaptor.capture());
usbTeardownOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -340,15 +338,15 @@ public class TetherInterfaceStateMachineTest {
initStateMachine(TETHERING_USB);
doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
IFACE_NAME, mInterfaceConfiguration);
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), mLinkPropertiesCaptor.capture());
usbTeardownOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -358,13 +356,13 @@ public class TetherInterfaceStateMachineTest {
doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
eq(mTestedSm), mLinkPropertiesCaptor.capture());
usbTeardownOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@@ -372,11 +370,11 @@ public class TetherInterfaceStateMachineTest {
public void ignoresDuplicateUpstreamNotifications() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
for (int i = 0; i < 5; i++) {
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
}
}
@@ -401,11 +399,11 @@ public class TetherInterfaceStateMachineTest {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any());
verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
}
private void assertDhcpStarted(IpPrefix expectedPrefix) {
verify(mTetheringDependencies, times(1)).makeDhcpServer(
verify(mDependencies, times(1)).makeDhcpServer(
eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
verify(mDhcpServer, times(1)).start();
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
@@ -422,21 +420,21 @@ public class TetherInterfaceStateMachineTest {
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
* @param command One of the TetherInterfaceStateMachine.CMD_* constants.
* @param command One of the IpServer.CMD_* constants.
* @param arg1 An additional argument to pass.
*/
private void dispatchCommand(int command, int arg1) {
mTestedSm.sendMessage(command, arg1);
mIpServer.sendMessage(command, arg1);
mLooper.dispatchAll();
}
/**
* Send a command to the state machine under test, and run the event loop to idle.
*
* @param command One of the TetherInterfaceStateMachine.CMD_* constants.
* @param command One of the IpServer.CMD_* constants.
*/
private void dispatchCommand(int command) {
mTestedSm.sendMessage(command);
mIpServer.sendMessage(command);
mLooper.dispatchAll();
}
@@ -447,7 +445,7 @@ public class TetherInterfaceStateMachineTest {
* @param upstreamIface String name of upstream interface (or null)
*/
private void dispatchTetherConnectionChanged(String upstreamIface) {
mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
new InterfaceSet(upstreamIface));
mLooper.dispatchAll();
}

View File

@@ -75,6 +75,7 @@ import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetworkConstants;
@@ -99,10 +100,8 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadHardwareInterface;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
@@ -190,7 +189,7 @@ public class TetheringTest {
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine upstreamNetworkMonitorMasterSM;
ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
ArrayList<IpServer> ipv6CoordinatorNotifyList;
int isTetheringSupportedCalls;
public void reset() {
@@ -213,29 +212,35 @@ public class TetheringTest {
@Override
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
ArrayList<IpServer> notifyList, SharedLog log) {
ipv6CoordinatorNotifyList = notifyList;
return mIPv6TetheringCoordinator;
}
@Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return mRouterAdvertisementDaemon;
}
public IpServer.Dependencies getIpServerDependencies() {
return new IpServer.Dependencies() {
@Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
InterfaceParams ifParams) {
return mRouterAdvertisementDaemon;
}
@Override
public INetd getNetdService() {
return mNetd;
}
@Override
public InterfaceParams getInterfaceParams(String ifName) {
final String[] ifaces = new String[] {
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
final int index = ArrayUtils.indexOf(ifaces, ifName);
assertTrue("Non-mocked interface: " + ifName, index >= 0);
return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
@Override
public InterfaceParams getInterfaceParams(String ifName) {
final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
TEST_MOBILE_IFNAME };
final int index = ArrayUtils.indexOf(ifaces, ifName);
assertTrue("Non-mocked interface: " + ifName, index >= 0);
return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
@Override
public INetd getNetdService() {
return mNetd;
}
};
}
@Override
@@ -458,9 +463,9 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
// If, and only if, Tethering received an interface status changed
// then it creates a TetherInterfaceStateMachine and sends out a
// broadcast indicating that the interface is "available".
// If, and only if, Tethering received an interface status changed then
// it creates a IpServer and sends out a broadcast indicating that the
// interface is "available".
if (emulateInterfaceStatusChanged) {
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
@@ -557,18 +562,18 @@ public class TetheringTest {
}
/**
* Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
* Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
*/
private void sendIPv6TetherUpdates(NetworkState upstreamState) {
// IPv6TetheringCoordinator must have been notified of downstream
verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IControlsTethering.STATE_TETHERED));
eq(IpServer.STATE_TETHERED));
for (TetherInterfaceStateMachine tism :
for (IpServer ipSrv :
mTetheringDependencies.ipv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIPv6Provisioned()
? ipv6OnlyState.linkProperties
: null);
@@ -812,7 +817,7 @@ public class TetheringTest {
// We verify get/set called thrice here: once for setup and twice during
// teardown because all events happen over the course of the single
// dispatchAll() above. Note that once the TISM IPv4 address config
// dispatchAll() above. Note that once the IpServer IPv4 address config
// code is refactored the two calls during shutdown will revert to one.
verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(3))