Merge "Fix race conditions between Tethering and TetherInterfaceStateMachine" into nyc-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ee90bb0b09
@@ -81,8 +81,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
|
||||||
@@ -126,7 +124,18 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
private final INetworkStatsService mStatsService;
|
private final INetworkStatsService mStatsService;
|
||||||
private final Looper mLooper;
|
private final Looper mLooper;
|
||||||
|
|
||||||
private Map<String, TetherInterfaceStateMachine> mIfaces; // all tethered/tetherable ifaces
|
private static class TetherState {
|
||||||
|
public final TetherInterfaceStateMachine mStateMachine;
|
||||||
|
public int mLastState;
|
||||||
|
public int mLastError;
|
||||||
|
public TetherState(TetherInterfaceStateMachine sm) {
|
||||||
|
mStateMachine = sm;
|
||||||
|
// Assume all state machines start out available and with no errors.
|
||||||
|
mLastState = IControlsTethering.STATE_AVAILABLE;
|
||||||
|
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private final ArrayMap<String, TetherState> mTetherStates;
|
||||||
|
|
||||||
private final BroadcastReceiver mStateReceiver;
|
private final BroadcastReceiver mStateReceiver;
|
||||||
|
|
||||||
@@ -174,7 +183,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
|
|
||||||
mPublicSync = new Object();
|
mPublicSync = new Object();
|
||||||
|
|
||||||
mIfaces = new ArrayMap<String, TetherInterfaceStateMachine>();
|
mTetherStates = new ArrayMap<>();
|
||||||
|
|
||||||
// make our own thread so we don't anr the system
|
// make our own thread so we don't anr the system
|
||||||
mLooper = IoThread.get().getLooper();
|
mLooper = IoThread.get().getLooper();
|
||||||
@@ -255,22 +264,20 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (up) {
|
if (up) {
|
||||||
if (sm == null) {
|
if (tetherState == null) {
|
||||||
sm = new TetherInterfaceStateMachine(iface, mLooper, interfaceType,
|
trackNewTetherableInterface(iface, interfaceType);
|
||||||
mNMService, mStatsService, this);
|
|
||||||
mIfaces.put(iface, sm);
|
|
||||||
sm.start();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (interfaceType == ConnectivityManager.TETHERING_USB) {
|
if (interfaceType == ConnectivityManager.TETHERING_USB) {
|
||||||
// ignore usb0 down after enabling RNDIS
|
// ignore usb0 down after enabling RNDIS
|
||||||
// we will handle disconnect in interfaceRemoved instead
|
// we will handle disconnect in interfaceRemoved instead
|
||||||
if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
|
if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
|
||||||
} else if (sm != null) {
|
} else if (tetherState != null) {
|
||||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
tetherState.mStateMachine.sendMessage(
|
||||||
mIfaces.remove(iface);
|
TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
||||||
|
mTetherStates.remove(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,15 +336,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (sm != null) {
|
if (tetherState == null) {
|
||||||
|
trackNewTetherableInterface(iface, interfaceType);
|
||||||
|
} else {
|
||||||
if (VDBG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
|
if (VDBG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
sm = new TetherInterfaceStateMachine(iface, mLooper, interfaceType,
|
|
||||||
mNMService, mStatsService, this);
|
|
||||||
mIfaces.put(iface, sm);
|
|
||||||
sm.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,15 +349,15 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
public void interfaceRemoved(String iface) {
|
public void interfaceRemoved(String iface) {
|
||||||
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
|
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (sm == null) {
|
if (tetherState == null) {
|
||||||
if (VDBG) {
|
if (VDBG) {
|
||||||
Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
|
Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
||||||
mIfaces.remove(iface);
|
mTetherStates.remove(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,19 +586,18 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
public int tether(String iface) {
|
public int tether(String iface) {
|
||||||
if (DBG) Log.d(TAG, "Tethering " + iface);
|
if (DBG) Log.d(TAG, "Tethering " + iface);
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (sm == null) {
|
if (tetherState == null) {
|
||||||
Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
|
Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
|
||||||
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||||
}
|
}
|
||||||
// Ignore the error status of the interface. If the interface is available,
|
// Ignore the error status of the interface. If the interface is available,
|
||||||
// the errors are referring to past tethering attempts anyway.
|
// the errors are referring to past tethering attempts anyway.
|
||||||
if (!sm.isAvailable()) {
|
if (tetherState.mLastState != IControlsTethering.STATE_AVAILABLE) {
|
||||||
Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
|
Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
|
||||||
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
|
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
|
||||||
|
|
||||||
}
|
}
|
||||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
||||||
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,43 +605,43 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
public int untether(String iface) {
|
public int untether(String iface) {
|
||||||
if (DBG) Log.d(TAG, "Untethering " + iface);
|
if (DBG) Log.d(TAG, "Untethering " + iface);
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (sm == null) {
|
if (tetherState == null) {
|
||||||
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
|
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
|
||||||
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||||
}
|
}
|
||||||
if (!sm.isTethered()) {
|
if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
|
||||||
Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
|
Log.e(TAG, "Tried to untether an untethered iface :" + iface + ", ignoring");
|
||||||
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
|
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
|
||||||
}
|
}
|
||||||
sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
tetherState.mStateMachine.sendMessage(
|
||||||
|
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
||||||
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void untetherAll() {
|
public void untetherAll() {
|
||||||
if (DBG) Log.d(TAG, "Untethering " + mIfaces);
|
synchronized (mPublicSync) {
|
||||||
for (String iface : mIfaces.keySet()) {
|
if (DBG) Log.d(TAG, "Untethering " + mTetherStates.keySet());
|
||||||
untether(iface);
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
|
untether(mTetherStates.keyAt(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLastTetherError(String iface) {
|
public int getLastTetherError(String iface) {
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
if (sm == null) {
|
if (tetherState == null) {
|
||||||
Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
|
Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
|
||||||
", ignoring");
|
", ignoring");
|
||||||
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||||
}
|
}
|
||||||
return sm.getLastError();
|
return tetherState.mLastError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - move all private methods used only by the state machine into the state machine
|
private void sendTetherStateChangedBroadcast() {
|
||||||
// to clarify what needs synchronized protection.
|
|
||||||
@Override
|
|
||||||
public void sendTetherStateChangedBroadcast() {
|
|
||||||
if (!getConnectivityManager().isTetheringSupported()) return;
|
if (!getConnectivityManager().isTetheringSupported()) return;
|
||||||
|
|
||||||
ArrayList<String> availableList = new ArrayList<String>();
|
ArrayList<String> availableList = new ArrayList<String>();
|
||||||
@@ -650,24 +653,22 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
boolean bluetoothTethered = false;
|
boolean bluetoothTethered = false;
|
||||||
|
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
Set<String> ifaces = mIfaces.keySet();
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
for (String iface : ifaces) {
|
TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
String iface = mTetherStates.keyAt(i);
|
||||||
if (sm != null) {
|
if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
||||||
if (sm.isErrored()) {
|
erroredList.add(iface);
|
||||||
erroredList.add(iface);
|
} else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
|
||||||
} else if (sm.isAvailable()) {
|
availableList.add(iface);
|
||||||
availableList.add(iface);
|
} else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
|
||||||
} else if (sm.isTethered()) {
|
if (isUsb(iface)) {
|
||||||
if (isUsb(iface)) {
|
usbTethered = true;
|
||||||
usbTethered = true;
|
} else if (isWifi(iface)) {
|
||||||
} else if (isWifi(iface)) {
|
wifiTethered = true;
|
||||||
wifiTethered = true;
|
} else if (isBluetooth(iface)) {
|
||||||
} else if (isBluetooth(iface)) {
|
bluetoothTethered = true;
|
||||||
bluetoothTethered = true;
|
|
||||||
}
|
|
||||||
activeList.add(iface);
|
|
||||||
}
|
}
|
||||||
|
activeList.add(iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -961,37 +962,27 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
public String[] getTetheredIfaces() {
|
public String[] getTetheredIfaces() {
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
Set<String> keys = mIfaces.keySet();
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
for (String key : keys) {
|
TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(key);
|
if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
|
||||||
if (sm.isTethered()) {
|
list.add(mTetherStates.keyAt(i));
|
||||||
list.add(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String[] retVal = new String[list.size()];
|
return list.toArray(new String[list.size()]);
|
||||||
for (int i=0; i < list.size(); i++) {
|
|
||||||
retVal[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getTetherableIfaces() {
|
public String[] getTetherableIfaces() {
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
Set<String> keys = mIfaces.keySet();
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
for (String key : keys) {
|
TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(key);
|
if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
|
||||||
if (sm.isAvailable()) {
|
list.add(mTetherStates.keyAt(i));
|
||||||
list.add(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String[] retVal = new String[list.size()];
|
return list.toArray(new String[list.size()]);
|
||||||
for (int i=0; i < list.size(); i++) {
|
|
||||||
retVal[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getTetheredDhcpRanges() {
|
public String[] getTetheredDhcpRanges() {
|
||||||
@@ -1001,19 +992,14 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
public String[] getErroredIfaces() {
|
public String[] getErroredIfaces() {
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
Set<String> keys = mIfaces.keySet();
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
for (String key : keys) {
|
TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(key);
|
if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
||||||
if (sm.isErrored()) {
|
list.add(mTetherStates.keyAt(i));
|
||||||
list.add(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String[] retVal = new String[list.size()];
|
return list.toArray(new String[list.size()]);
|
||||||
for (int i= 0; i< list.size(); i++) {
|
|
||||||
retVal[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeLogMessage(State state, int what) {
|
private void maybeLogMessage(State state, int what) {
|
||||||
@@ -1143,6 +1129,18 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
private State mStopTetheringErrorState;
|
private State mStopTetheringErrorState;
|
||||||
private State mSetDnsForwardersErrorState;
|
private State mSetDnsForwardersErrorState;
|
||||||
|
|
||||||
|
// This list is a little subtle. It contains all the interfaces that currently are
|
||||||
|
// requesting tethering, regardless of whether these interfaces are still members of
|
||||||
|
// mTetherStates. This allows us to maintain the following predicates:
|
||||||
|
//
|
||||||
|
// 1) mTetherStates contains the set of all currently existing, tetherable, link state up
|
||||||
|
// interfaces.
|
||||||
|
// 2) mNotifyList contains all state machines that may have outstanding tethering state
|
||||||
|
// that needs to be torn down.
|
||||||
|
//
|
||||||
|
// 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 ArrayList<TetherInterfaceStateMachine> mNotifyList;
|
private ArrayList<TetherInterfaceStateMachine> mNotifyList;
|
||||||
|
|
||||||
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
|
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
|
||||||
@@ -1453,15 +1451,16 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
|
config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
|
||||||
ArrayList<Integer> tethered = new ArrayList<Integer>();
|
ArrayList<Integer> tethered = new ArrayList<Integer>();
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
Set<String> ifaces = mIfaces.keySet();
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
for (String iface : ifaces) {
|
TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
TetherInterfaceStateMachine sm = mIfaces.get(iface);
|
if (tetherState.mLastState !=
|
||||||
if (sm != null && sm.isTethered()) {
|
IControlsTethering.STATE_TETHERED) {
|
||||||
int interfaceType = ifaceNameToType(iface);
|
continue; // Skip interfaces that aren't tethered.
|
||||||
if (interfaceType !=
|
}
|
||||||
ConnectivityManager.TETHERING_INVALID) {
|
String iface = mTetherStates.keyAt(i);
|
||||||
tethered.add(new Integer(interfaceType));
|
int interfaceType = ifaceNameToType(iface);
|
||||||
}
|
if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
|
||||||
|
tethered.add(new Integer(interfaceType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1487,9 +1486,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
}
|
}
|
||||||
|
|
||||||
class InitialState extends TetherMasterUtilState {
|
class InitialState extends TetherMasterUtilState {
|
||||||
@Override
|
|
||||||
public void enter() {
|
|
||||||
}
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
maybeLogMessage(this, message.what);
|
maybeLogMessage(this, message.what);
|
||||||
@@ -1498,16 +1494,15 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
case CMD_TETHER_MODE_REQUESTED:
|
case CMD_TETHER_MODE_REQUESTED:
|
||||||
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
|
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
|
||||||
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
|
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
|
||||||
mNotifyList.add(who);
|
if (mNotifyList.indexOf(who) < 0) {
|
||||||
|
mNotifyList.add(who);
|
||||||
|
}
|
||||||
transitionTo(mTetherModeAliveState);
|
transitionTo(mTetherModeAliveState);
|
||||||
break;
|
break;
|
||||||
case CMD_TETHER_MODE_UNREQUESTED:
|
case CMD_TETHER_MODE_UNREQUESTED:
|
||||||
who = (TetherInterfaceStateMachine)message.obj;
|
who = (TetherInterfaceStateMachine)message.obj;
|
||||||
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
|
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
|
||||||
int index = mNotifyList.indexOf(who);
|
mNotifyList.remove(who);
|
||||||
if (index != -1) {
|
|
||||||
mNotifyList.remove(who);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
retValue = false;
|
retValue = false;
|
||||||
@@ -1546,24 +1541,26 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
case CMD_TETHER_MODE_REQUESTED:
|
case CMD_TETHER_MODE_REQUESTED:
|
||||||
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
|
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
|
||||||
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
|
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
|
||||||
mNotifyList.add(who);
|
if (mNotifyList.indexOf(who) < 0) {
|
||||||
|
mNotifyList.add(who);
|
||||||
|
}
|
||||||
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
|
||||||
mCurrentUpstreamIface);
|
mCurrentUpstreamIface);
|
||||||
break;
|
break;
|
||||||
case CMD_TETHER_MODE_UNREQUESTED:
|
case CMD_TETHER_MODE_UNREQUESTED:
|
||||||
who = (TetherInterfaceStateMachine)message.obj;
|
who = (TetherInterfaceStateMachine)message.obj;
|
||||||
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
|
if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
|
||||||
int index = mNotifyList.indexOf(who);
|
if (mNotifyList.remove(who)) {
|
||||||
if (index != -1) {
|
|
||||||
if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who);
|
if (DBG) Log.d(TAG, "TetherModeAlive removing notifyee " + who);
|
||||||
mNotifyList.remove(index);
|
|
||||||
if (mNotifyList.isEmpty()) {
|
if (mNotifyList.isEmpty()) {
|
||||||
turnOffMasterTetherSettings(); // transitions appropriately
|
turnOffMasterTetherSettings(); // transitions appropriately
|
||||||
} else {
|
} else {
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
|
Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
|
||||||
" live requests:");
|
" live requests:");
|
||||||
for (Object o : mNotifyList) Log.d(TAG, " " + o);
|
for (TetherInterfaceStateMachine o : mNotifyList) {
|
||||||
|
Log.d(TAG, " " + o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1623,8 +1620,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
}
|
}
|
||||||
void notify(int msgType) {
|
void notify(int msgType) {
|
||||||
mErrorNotification = msgType;
|
mErrorNotification = msgType;
|
||||||
for (Object o : mNotifyList) {
|
for (TetherInterfaceStateMachine sm : mNotifyList) {
|
||||||
TetherInterfaceStateMachine sm = (TetherInterfaceStateMachine)o;
|
|
||||||
sm.sendMessage(msgType);
|
sm.sendMessage(msgType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1707,8 +1703,26 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
|
|
||||||
pw.println("Tether state:");
|
pw.println("Tether state:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
for (Object o : mIfaces.values()) {
|
for (int i = 0; i < mTetherStates.size(); i++) {
|
||||||
pw.println(o);
|
final String iface = mTetherStates.keyAt(i);
|
||||||
|
final TetherState tetherState = mTetherStates.valueAt(i);
|
||||||
|
pw.print(iface + " - ");
|
||||||
|
|
||||||
|
switch (tetherState.mLastState) {
|
||||||
|
case IControlsTethering.STATE_UNAVAILABLE:
|
||||||
|
pw.print("UnavailableState");
|
||||||
|
break;
|
||||||
|
case IControlsTethering.STATE_AVAILABLE:
|
||||||
|
pw.print("AvailableState");
|
||||||
|
break;
|
||||||
|
case IControlsTethering.STATE_TETHERED:
|
||||||
|
pw.print("TetheredState");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pw.print("UnknownState");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pw.println(" - lastError = " + tetherState.mLastError);
|
||||||
}
|
}
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
}
|
}
|
||||||
@@ -1716,9 +1730,40 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyInterfaceTetheringReadiness(boolean isReady,
|
public void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
|
||||||
TetherInterfaceStateMachine who) {
|
int state, int error) {
|
||||||
mTetherMasterSM.sendMessage((isReady) ? TetherMasterSM.CMD_TETHER_MODE_REQUESTED
|
synchronized (mPublicSync) {
|
||||||
: TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, who);
|
TetherState tetherState = mTetherStates.get(iface);
|
||||||
|
if (tetherState != null && tetherState.mStateMachine.equals(who)) {
|
||||||
|
tetherState.mLastState = state;
|
||||||
|
tetherState.mLastError = error;
|
||||||
|
} else {
|
||||||
|
if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "iface " + iface + " notified that it was in state " + state +
|
||||||
|
" with error " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case IControlsTethering.STATE_UNAVAILABLE:
|
||||||
|
case IControlsTethering.STATE_AVAILABLE:
|
||||||
|
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED, who);
|
||||||
|
break;
|
||||||
|
case IControlsTethering.STATE_TETHERED:
|
||||||
|
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED, who);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendTetherStateChangedBroadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trackNewTetherableInterface(String iface, int interfaceType) {
|
||||||
|
TetherState tetherState;
|
||||||
|
tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper,
|
||||||
|
interfaceType, mNMService, mStatsService, this));
|
||||||
|
mTetherStates.put(iface, tetherState);
|
||||||
|
tetherState.mStateMachine.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ package com.android.server.connectivity.tethering;
|
|||||||
* Interface with methods necessary to notify that a given interface is ready for tethering.
|
* Interface with methods necessary to notify that a given interface is ready for tethering.
|
||||||
*/
|
*/
|
||||||
public interface IControlsTethering {
|
public interface IControlsTethering {
|
||||||
void sendTetherStateChangedBroadcast();
|
public final int STATE_UNAVAILABLE = 0;
|
||||||
void notifyInterfaceTetheringReadiness(boolean isReady, TetherInterfaceStateMachine who);
|
public final int STATE_AVAILABLE = 1;
|
||||||
|
public final int STATE_TETHERED = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that |who| has changed its tethering state. This may be called from any thread.
|
||||||
|
*
|
||||||
|
* @param iface a network interface (e.g. "wlan0")
|
||||||
|
* @param who corresponding instance of a TetherInterfaceStateMachine
|
||||||
|
* @param state one of IControlsTethering.STATE_*
|
||||||
|
* @param lastError one of ConnectivityManager.TETHER_ERROR_*
|
||||||
|
*/
|
||||||
|
void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
|
||||||
|
int state, int lastError);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import android.os.Message;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.internal.util.IState;
|
|
||||||
import com.android.internal.util.MessageUtils;
|
import com.android.internal.util.MessageUtils;
|
||||||
import com.android.internal.util.Protocol;
|
import com.android.internal.util.Protocol;
|
||||||
import com.android.internal.util.State;
|
import com.android.internal.util.State;
|
||||||
@@ -98,7 +97,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
mTetherController = tetherController;
|
mTetherController = tetherController;
|
||||||
mIfaceName = ifaceName;
|
mIfaceName = ifaceName;
|
||||||
mInterfaceType = interfaceType;
|
mInterfaceType = interfaceType;
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
|
||||||
mInitialState = new InitialState();
|
mInitialState = new InitialState();
|
||||||
addState(mInitialState);
|
addState(mInitialState);
|
||||||
@@ -110,40 +109,6 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
setInitialState(mInitialState);
|
setInitialState(mInitialState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String res = new String();
|
|
||||||
res += mIfaceName + " - ";
|
|
||||||
IState current = getCurrentState();
|
|
||||||
if (current == mInitialState) res += "InitialState";
|
|
||||||
if (current == mTetheredState) res += "TetheredState";
|
|
||||||
if (current == mUnavailableState) res += "UnavailableState";
|
|
||||||
if (isAvailable()) res += " - Available";
|
|
||||||
if (isTethered()) res += " - Tethered";
|
|
||||||
res += " - lastError =" + getLastError();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLastError() {
|
|
||||||
return mLastError;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLastError(int error) {
|
|
||||||
mLastError = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return getCurrentState() == mInitialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTethered() {
|
|
||||||
return getCurrentState() == mTetheredState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isErrored() {
|
|
||||||
return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// configured when we start tethering and unconfig'd on error or conclusion
|
// configured when we start tethering and unconfig'd on error or conclusion
|
||||||
private boolean configureIfaceIp(boolean enabled) {
|
private boolean configureIfaceIp(boolean enabled) {
|
||||||
if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
|
if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
|
||||||
@@ -193,7 +158,9 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
class InitialState extends State {
|
class InitialState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
mTetherController.sendTetherStateChangedBroadcast();
|
mTetherController.notifyInterfaceStateChange(
|
||||||
|
mIfaceName, TetherInterfaceStateMachine.this,
|
||||||
|
IControlsTethering.STATE_AVAILABLE, mLastError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -202,8 +169,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
boolean retValue = true;
|
boolean retValue = true;
|
||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case CMD_TETHER_REQUESTED:
|
case CMD_TETHER_REQUESTED:
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
mTetherController.notifyInterfaceTetheringReadiness(true, TetherInterfaceStateMachine.this);
|
|
||||||
transitionTo(mTetheredState);
|
transitionTo(mTetheredState);
|
||||||
break;
|
break;
|
||||||
case CMD_INTERFACE_DOWN:
|
case CMD_INTERFACE_DOWN:
|
||||||
@@ -221,7 +187,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
if (!configureIfaceIp(true)) {
|
if (!configureIfaceIp(true)) {
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
transitionTo(mInitialState);
|
transitionTo(mInitialState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,19 +196,18 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
mNMService.tetherInterface(mIfaceName);
|
mNMService.tetherInterface(mIfaceName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Error Tethering: " + e.toString());
|
Log.e(TAG, "Error Tethering: " + e.toString());
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
transitionTo(mInitialState);
|
transitionTo(mInitialState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
|
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
|
||||||
mTetherController.sendTetherStateChangedBroadcast();
|
mTetherController.notifyInterfaceStateChange(
|
||||||
|
mIfaceName, TetherInterfaceStateMachine.this,
|
||||||
|
IControlsTethering.STATE_TETHERED, mLastError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exit() {
|
public void exit() {
|
||||||
mTetherController.notifyInterfaceTetheringReadiness(false,
|
|
||||||
TetherInterfaceStateMachine.this);
|
|
||||||
|
|
||||||
// Note that at this point, we're leaving the tethered state. We can fail any
|
// Note that at this point, we're leaving the tethered state. We can fail any
|
||||||
// of these operations, but it doesn't really change that we have to try them
|
// of these operations, but it doesn't really change that we have to try them
|
||||||
// all in sequence.
|
// all in sequence.
|
||||||
@@ -251,7 +216,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
try {
|
try {
|
||||||
mNMService.untetherInterface(mIfaceName);
|
mNMService.untetherInterface(mIfaceName);
|
||||||
} catch (Exception ee) {
|
} catch (Exception ee) {
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
||||||
Log.e(TAG, "Failed to untether interface: " + ee.toString());
|
Log.e(TAG, "Failed to untether interface: " + ee.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +280,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
newUpstreamIfaceName);
|
newUpstreamIfaceName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Exception enabling Nat: " + e.toString());
|
Log.e(TAG, "Exception enabling Nat: " + e.toString());
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
|
||||||
transitionTo(mInitialState);
|
transitionTo(mInitialState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -327,8 +292,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
case CMD_START_TETHERING_ERROR:
|
case CMD_START_TETHERING_ERROR:
|
||||||
case CMD_STOP_TETHERING_ERROR:
|
case CMD_STOP_TETHERING_ERROR:
|
||||||
case CMD_SET_DNS_FORWARDERS_ERROR:
|
case CMD_SET_DNS_FORWARDERS_ERROR:
|
||||||
setLastErrorAndTransitionToInitialState(
|
mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
|
||||||
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
|
transitionTo(mInitialState);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
retValue = false;
|
retValue = false;
|
||||||
@@ -348,13 +313,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
|
|||||||
class UnavailableState extends State {
|
class UnavailableState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
|
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
mTetherController.sendTetherStateChangedBroadcast();
|
mTetherController.notifyInterfaceStateChange(
|
||||||
|
mIfaceName, TetherInterfaceStateMachine.this,
|
||||||
|
IControlsTethering.STATE_UNAVAILABLE, mLastError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLastErrorAndTransitionToInitialState(int error) {
|
|
||||||
setLastError(error);
|
|
||||||
transitionTo(mInitialState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.tethering;
|
package com.android.server.connectivity.tethering;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
@@ -26,6 +24,14 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
|
||||||
|
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
|
import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
||||||
|
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 android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.INetworkStatsService;
|
import android.net.INetworkStatsService;
|
||||||
import android.net.InterfaceConfiguration;
|
import android.net.InterfaceConfiguration;
|
||||||
@@ -78,8 +84,7 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
|
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before public void setUp() throws Exception {
|
||||||
public void setUp() throws Exception {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,10 +94,8 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper);
|
ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper);
|
||||||
mTestedSm.start();
|
mTestedSm.start();
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
assertTrue("Should start out available for tethering", mTestedSm.isAvailable());
|
verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
assertFalse("Should not be tethered initially", mTestedSm.isTethered());
|
IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
assertFalse("Should have no errors initially", mTestedSm.isErrored());
|
|
||||||
verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
|
||||||
verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
|
verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,28 +122,23 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
@Test
|
@Test
|
||||||
public void handlesImmediateInterfaceDown() throws Exception {
|
public void handlesImmediateInterfaceDown() throws Exception {
|
||||||
initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
|
initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
|
||||||
|
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
||||||
verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertFalse("Should not be tetherable when the interface is down", mTestedSm.isAvailable());
|
|
||||||
assertFalse("Should not be tethered when the interface is down", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors when the interface goes immediately down",
|
|
||||||
mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canBeTethered() throws Exception {
|
public void canBeTethered() throws Exception {
|
||||||
initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
|
initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
|
||||||
|
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
||||||
InOrder inOrder = inOrder(mTetherHelper, mNMService);
|
InOrder inOrder = inOrder(mTetherHelper, mNMService);
|
||||||
inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm);
|
|
||||||
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
|
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
|
||||||
inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertFalse("Should not be tetherable when tethered", mTestedSm.isAvailable());
|
|
||||||
assertTrue("Should be in a tethered state", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors when tethered", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -149,13 +147,10 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
|
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
||||||
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
|
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
|
||||||
inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm);
|
|
||||||
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
|
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
|
||||||
inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertTrue("Should be ready for tethering again", mTestedSm.isAvailable());
|
|
||||||
assertFalse("Should not be tethered", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -164,16 +159,12 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
|
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
||||||
InOrder inOrder = inOrder(mTetherHelper, mNMService);
|
InOrder inOrder = inOrder(mTetherHelper, mNMService);
|
||||||
inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm);
|
|
||||||
inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
|
inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
|
||||||
inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
|
inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
|
||||||
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
|
inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
|
||||||
inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertFalse("Should not be tetherable when tethered", mTestedSm.isAvailable());
|
|
||||||
assertTrue("Should be in a tethered state", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors when tethered", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -186,9 +177,6 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertFalse("Should not be tetherable when tethered", mTestedSm.isAvailable());
|
|
||||||
assertTrue("Should be in a tethered state", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors when tethered", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -203,9 +191,6 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
|
inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
|
||||||
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
|
inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertFalse("Should not be tetherable when tethered", mTestedSm.isAvailable());
|
|
||||||
assertTrue("Should be in a tethered state", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors when tethered", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -214,16 +199,13 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
|
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
|
||||||
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
|
InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
|
||||||
inOrder.verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm);
|
|
||||||
inOrder.verify(mStatsService).forceUpdate();
|
inOrder.verify(mStatsService).forceUpdate();
|
||||||
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
|
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
|
||||||
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
|
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
|
||||||
inOrder.verify(mTetherHelper).sendTetherStateChangedBroadcast();
|
inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
|
||||||
assertTrue("Should be ready for tethering again", mTestedSm.isAvailable());
|
|
||||||
assertFalse("Should not be tethered", mTestedSm.isTethered());
|
|
||||||
assertFalse("Should have no errors", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -235,13 +217,12 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
|
doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
|
||||||
}
|
}
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
|
||||||
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration);
|
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
|
||||||
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
||||||
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
|
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
|
||||||
IFACE_NAME, mInterfaceConfiguration);
|
IFACE_NAME, mInterfaceConfiguration);
|
||||||
verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm);
|
usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
assertFalse("Should not be available", mTestedSm.isAvailable());
|
IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
assertFalse("Should not be tethered", mTestedSm.isTethered());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,15 +232,12 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
|
|
||||||
doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
|
doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
|
||||||
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
|
||||||
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration);
|
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
|
||||||
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
||||||
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
|
usbTeardownOrder.verify(mNMService).setInterfaceConfig(
|
||||||
IFACE_NAME, mInterfaceConfiguration);
|
IFACE_NAME, mInterfaceConfiguration);
|
||||||
// Initial call is when we transition to the tethered state on request.
|
usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
verify(mTetherHelper).notifyInterfaceTetheringReadiness(true, mTestedSm);
|
IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
|
||||||
// And this call is to notify that we really aren't requested tethering.
|
|
||||||
verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm);
|
|
||||||
assertTrue("Expected to see an error reported", mTestedSm.isErrored());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -268,10 +246,11 @@ public class TetherInterfaceStateMachineTest {
|
|||||||
|
|
||||||
doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
|
doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration);
|
InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
|
||||||
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
|
||||||
usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
|
usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
|
||||||
verify(mTetherHelper).notifyInterfaceTetheringReadiness(false, mTestedSm);
|
usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
|
||||||
|
IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user