Merge "Add USB RNDIS enable/disable control"

This commit is contained in:
Robert Greenwalt
2010-02-23 18:08:04 -08:00
committed by Android (Google) Code Review
4 changed files with 238 additions and 32 deletions

View File

@@ -256,14 +256,14 @@ class NetworkManagementService extends INetworkManagementService.Stub {
Log.e(TAG, "Failed to parse netmask", uhe);
cfg.netmask = 0;
}
cfg.interfaceFlags = st.nextToken("]");
cfg.interfaceFlags = st.nextToken("]").trim() +"]";
Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
return cfg;
}
public void setInterfaceConfig(
String iface, InterfaceConfiguration cfg) throws IllegalStateException {
String cmd = String.format("interface setcfg %s %s %s", iface,
String cmd = String.format("interface setcfg %s %s %s %s", iface,
intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
mConnector.doCommand(cmd);
}

View File

@@ -27,6 +27,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
@@ -56,6 +57,7 @@ import java.util.Set;
*
* TODO - look for parent classes and code sharing
*/
public class Tethering extends INetworkManagementEventObserver.Stub {
private Notification mTetheringNotification;
@@ -80,6 +82,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private String mUpstreamIfaceName;
// turning on/off RNDIS resets the interface generating and extra discon/conn cycle
// count how many to ignore.. Self correcting if you plug/unplug a bunch of times.
private int mUsbResetExpected = 0;
HierarchicalStateMachine mTetherMasterSM;
public Tethering(Context context) {
@@ -113,7 +119,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
com.android.internal.R.array.config_tether_dhcp_range);
if (mDhcpRange.length == 0) {
mDhcpRange = new String[2];
mDhcpRange[0] = new String("169.254.2.1");
mDhcpRange[0] = new String("169.254.2.2");
mDhcpRange[1] = new String("169.254.2.64");
} else if(mDhcpRange.length == 1) {
String[] tmp = new String[2];
@@ -127,16 +133,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mTetherableWifiRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_wifi_regexs);
String[] ifaces = new String[0];
try {
ifaces = service.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
}
for (String iface : ifaces) {
interfaceAdded(iface);
}
// TODO - remove and rely on real notifications of the current iface
mDnsServers = new String[2];
mDnsServers[0] = "8.8.8.8";
@@ -147,6 +143,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void interfaceLinkStatusChanged(String iface, boolean link) {
Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
boolean found = false;
boolean usb = false;
for (String regex : mTetherableWifiRegexs) {
if (iface.matches(regex)) {
found = true;
@@ -156,6 +153,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
for (String regex: mTetherableUsbRegexs) {
if (iface.matches(regex)) {
found = true;
usb = true;
break;
}
}
@@ -165,7 +163,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
TetherInterfaceSM sm = mIfaces.get(iface);
if (link) {
if (sm == null) {
sm = new TetherInterfaceSM(iface);
sm = new TetherInterfaceSM(iface, usb);
mIfaces.put(iface, sm);
sm.start();
}
@@ -179,7 +177,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
public void interfaceAdded(String iface) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
boolean found = false;
boolean usb = false;
for (String regex : mTetherableWifiRegexs) {
if (iface.matches(regex)) {
found = true;
@@ -189,6 +190,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
found = true;
usb = true;
break;
}
}
@@ -196,13 +198,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
Log.d(TAG, iface + " is not a tetherable iface, ignoring");
return;
}
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
return;
}
sm = new TetherInterfaceSM(iface);
sm = new TetherInterfaceSM(iface, usb);
mIfaces.put(iface, sm);
sm.start();
}
@@ -262,6 +265,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
private void sendTetherStateChangedBroadcast() {
Log.e("RJGRJG", "sendTetherStateChangedBroadcast");
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
@@ -314,8 +318,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
if (tellUser) {
for (Object o : availableList) {
String s = (String)o;
for (Object matchObject : mTetherableUsbRegexs) {
if (s.matches((String)matchObject)) {
for (String match : mTetherableUsbRegexs) {
if (s.matches(match)) {
showTetherAvailableNotification();
return;
}
@@ -414,20 +418,33 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
Tethering.this.handleTtyConnect();
Log.w(TAG, "got UMS connected");
synchronized (Tethering.this) {
if(mUsbResetExpected != 0) {
Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
mUsbResetExpected--;
return;
}
}
Tethering.this.toggleUsbIfaces(true); // add them
} else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
Tethering.this.handleTtyDisconnect();
Log.w(TAG, "got UMS disconneded broadcast");
synchronized (Tethering.this) {
if(mUsbResetExpected != 0) {
Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
mUsbResetExpected--;
return;
}
}
Tethering.this.toggleUsbIfaces(false); // remove them
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
Log.e("RJGRJG", "got conn action :"+action);
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service =
IConnectivityManager.Stub.asInterface(b);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
NetworkInfo info = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
int msg;
@@ -442,6 +459,114 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
// used on cable insert/remove
private void toggleUsbIfaces(boolean add) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] ifaces = new String[0];
try {
ifaces = service.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return;
}
for (String iface : ifaces) {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
if (add) {
interfaceAdded(iface);
} else {
interfaceRemoved(iface);
}
}
}
}
}
// toggled when we enter/leave the fully teathered state
private boolean enableRndisUsb(boolean enabled) {
Log.d(TAG, "enableRndisUsb(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
if (enabled) {
// turning this on will reset USB and generate two bogus events - ignore them
synchronized (this) {
if (!service.isUsbRNDISStarted()) {
mUsbResetExpected += 2;
service.startUsbRNDIS();
}
}
} else {
if (service.isUsbRNDISStarted()) {
service.stopUsbRNDIS();
}
}
} catch (Exception e) {
Log.e(TAG, "Error toggling usb RNDIS :" + e);
return false;
}
return true;
}
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsb(boolean enabled) {
Log.d(TAG, "configureUsb(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
// bring toggle the interfaces
String[] ifaces = new String[0];
try {
ifaces = service.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return false;
}
for (String iface : ifaces) {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
InterfaceConfiguration ifcg = null;
try {
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
if (enabled) {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
}
service.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
return false;
}
}
}
}
if (!enabled) {
// turn off ndis
try {
synchronized (this) {
if (service.isUsbRNDISStarted()) {
mUsbResetExpected += 2;
service.stopUsbRNDIS();
}
}
} catch (Exception e) {
Log.e(TAG, "Error stopping usb RNDIS :" + e);
return false;
}
}
return true;
}
private void handleTtyConnect() {
Log.d(TAG, "handleTtyConnect");
// for each of the available Tty not already supported by a ppp session,
@@ -609,6 +734,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private HierarchicalState mUntetherInterfaceErrorState;
private HierarchicalState mEnableNatErrorState;
private HierarchicalState mDisableNatErrorState;
private HierarchicalState mUsbConfigurationErrorState;
private HierarchicalState mUnavailableState;
@@ -617,10 +743,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean mTethered;
String mIfaceName;
boolean mUsb;
TetherInterfaceSM(String name) {
TetherInterfaceSM(String name, boolean usb) {
super(name);
mIfaceName = name;
mUsb = usb;
mInitialState = new InitialState();
addState(mInitialState);
@@ -638,6 +766,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
addState(mEnableNatErrorState);
mDisableNatErrorState = new DisableNatErrorState();
addState(mDisableNatErrorState);
mUsbConfigurationErrorState = new UsbConfigurationErrorState();
addState(mUsbConfigurationErrorState);
mUnavailableState = new UnavailableState();
addState(mUnavailableState);
@@ -656,6 +786,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState";
if (current == mEnableNatErrorState) res += "EnableNatErrorState";
if (current == mDisableNatErrorState) res += "DisableNatErrorState";
if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState";
if (current == mUnavailableState) res += "UnavailableState";
if (mAvailable) res += " - Available";
if (mTethered) res += " - Tethered";
@@ -686,8 +817,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return mErrored;
}
private synchronized void setErrored(boolean errored) {
mErrored = errored;
private void setErrored(boolean errored) {
synchronized (this) {
mErrored = errored;
}
if (errored && mUsb) {
// note everything's been unwound by this point so nothing to do on
// further error..
Tethering.this.configureUsb(false);
}
}
class InitialState extends HierarchicalState {
@@ -726,6 +864,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
@Override
public void enter() {
setAvailable(false);
if (mUsb) {
if (!Tethering.this.configureUsb(true)) {
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
m = obtainMessage(CMD_TRANSITION_TO_ERROR);
m.obj = mUsbConfigurationErrorState;
sendMessageAtFrontOfQueue(m);
return;
}
}
sendTetherStateChangedBroadcast();
}
@Override
@@ -739,6 +890,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
if (mUsb) {
if (!Tethering.this.configureUsb(false)) {
transitionTo(mUsbConfigurationErrorState);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_TETHER_MODE_ALIVE:
@@ -759,6 +916,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mTetherMasterSM.sendMessage(m);
transitionTo(mUnavailableState);
break;
case CMD_TRANSITION_TO_ERROR:
HierarchicalState s = (HierarchicalState)(message.obj);
transitionTo(s);
break;
default:
retValue = false;
}
@@ -788,12 +949,17 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
sendMessageAtFrontOfQueue(m);
return;
}
if (mUsb) Tethering.this.enableRndisUsb(true);
Log.d(TAG, "Tethered " + mIfaceName);
setAvailable(false);
setTethered(true);
sendTetherStateChangedBroadcast();
}
@Override
public void exit() {
if(mUsb) Tethering.this.enableRndisUsb(false);
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "TetheredState.processMessage what=" + message.what);
boolean retValue = true;
@@ -821,6 +987,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!Tethering.this.configureUsb(false)) {
transitionTo(mUsbConfigurationErrorState);
} else {
transitionTo(mUsbConfigurationErrorState);
}
}
transitionTo(mInitialState);
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
@@ -857,6 +1030,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
sendTetherStateChangedBroadcast();
if (mUsb) {
if (!Tethering.this.configureUsb(false)) {
transitionTo(mUsbConfigurationError);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_TRANSITION_TO_ERROR:
@@ -974,6 +1153,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
sendTetherStateChangedBroadcast();
}
}
class UsbConfigurationErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to configure USB " + mIfaceName);
setAvailable(false);
setErrored(true);
}
}
}
class TetherMasterSM extends HierarchicalStateMachine {
@@ -1179,6 +1367,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
class CellDunAliveState extends HierarchicalState {
@Override
public void enter() {
Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
}
@@ -1228,6 +1417,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
transitionTo(mInitialState);
break;
case CMD_CELL_DUN_RENEW:
Log.d(TAG, "renewing dun connection - requeuing for another " +
CELL_DUN_RENEW_MS + "ms");
b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
try {
@@ -1248,6 +1439,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
class TetherModeAliveState extends HierarchicalState {
@Override
public void enter() {
Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
for (Object o : mNotifyList) {
TetherInterfaceSM sm = (TetherInterfaceSM)o;
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
@@ -1298,6 +1491,18 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
transitionTo(mInitialState);
break;
case CMD_CELL_DUN_RENEW:
Log.d(TAG, "renewing dun connection - requeuing for another " +
CELL_DUN_RENEW_MS + "ms");
b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
try {
cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
Phone.FEATURE_ENABLE_DUN, new Binder());
} catch (Exception e) {
}
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
break;
default:
retValue = false;
break;