Extract TetherInterfaceSM to its own class.

Attempt to keep all existing logic in place, except:

 + Marked a constructor as public, rather than default visible.
 + Added TAG, DBG, VDBG, and decoder ringer statics.
 + Moved static constants related to USB IPs into TetherInterfaceSM.

Bug: 28833951
Test: WiFi Tethering works on angler.

Change-Id: Id961220a9045832354cfe7381e5e9c0d8f54bf90
This commit is contained in:
Christopher Wiley
2016-05-19 11:54:54 -07:00
parent 9a509ca728
commit e3f93b02bd
3 changed files with 485 additions and 442 deletions

View File

@@ -71,6 +71,7 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.TetherInterfaceSM;
import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor;
@@ -135,9 +136,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
private static final int USB_PREFIX_LENGTH = 24;
// USB is 192.168.42.1 and 255.255.255.0
// Wifi is 192.168.43.1 and 255.255.255.0
// BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
@@ -980,442 +978,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
}
}
/**
* @hide
*
* Tracks the eligibility of a given network interface for tethering.
*/
public static class TetherInterfaceSM extends StateMachine {
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// notification from the master SM that it's not in tether mode
static final int CMD_TETHER_MODE_DEAD = BASE_IFACE + 1;
// request from the user that it wants to tether
static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
// request from the user that it wants to untether
static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
// notification that this interface is down
static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
// notification that this interface is up
static final int CMD_INTERFACE_UP = BASE_IFACE + 5;
// notification from the master SM that it had an error turning on cellular dun
static final int CMD_CELL_DUN_ERROR = BASE_IFACE + 6;
// notification from the master SM that it had trouble enabling IP Forwarding
static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
// notification from the master SM that it had trouble disabling IP Forwarding
static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
// notification from the master SM that it had trouble starting tethering
static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
// notification from the master SM that it had trouble stopping tethering
static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
// notification from the master SM that it had trouble setting the DNS forwarders
static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
// the upstream connection has changed
static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
private final State mInitialState;
private final State mStartingState;
private final State mTetheredState;
private final State mUnavailableState;
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final IControlsTethering mTetherController;
private final boolean mUsb;
private final String mIfaceName;
private final Object mMutex; // Protects the fields below.
private boolean mAvailable;
private boolean mTethered;
private int mLastError;
private String mMyUpstreamIfaceName; // may change over time
TetherInterfaceSM(String ifaceName, Looper looper, boolean usb, Object mutex,
INetworkManagementService nMService, INetworkStatsService statsService,
IControlsTethering tetherController) {
super(ifaceName, looper);
mNMService = nMService;
mStatsService = statsService;
mTetherController = tetherController;
mIfaceName = ifaceName;
mUsb = usb;
mMutex = mutex;
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
mInitialState = new InitialState();
addState(mInitialState);
mStartingState = new StartingState();
addState(mStartingState);
mTetheredState = new TetheredState();
addState(mTetheredState);
mUnavailableState = new UnavailableState();
addState(mUnavailableState);
setInitialState(mInitialState);
}
@Override
public String toString() {
String res = new String();
res += mIfaceName + " - ";
IState current = getCurrentState();
if (current == mInitialState) res += "InitialState";
if (current == mStartingState) res += "StartingState";
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() {
synchronized (mMutex) {
return mLastError;
}
}
private void setLastError(int error) {
synchronized (mMutex) {
mLastError = error;
if (isErrored()) {
if (mUsb) {
// note everything's been unwound by this point so nothing to do on
// further error..
configureUsbIface(false, mIfaceName);
}
}
}
}
public boolean isAvailable() {
synchronized (mMutex) {
return mAvailable;
}
}
private void setAvailable(boolean available) {
synchronized (mMutex) {
mAvailable = available;
}
}
public boolean isTethered() {
synchronized (mMutex) {
return mTethered;
}
}
private void setTethered(boolean tethered) {
synchronized (mMutex) {
mTethered = tethered;
}
}
public boolean isErrored() {
synchronized (mMutex) {
return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
}
}
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsbIface(boolean enabled, String iface) {
if (VDBG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
InterfaceConfiguration ifcg = null;
try {
ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
ifcg.setLinkAddress(new LinkAddress(addr, USB_PREFIX_LENGTH));
if (enabled) {
ifcg.setInterfaceUp();
} else {
ifcg.setInterfaceDown();
}
ifcg.clearFlag("running");
mNMService.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface, e);
return false;
}
return true;
}
private void maybeLogMessage(State state, int what) {
if (DBG) {
Log.d(TAG, state.getName() + " got " +
sMagicDecoderRing.get(what, Integer.toString(what)));
}
}
class InitialState extends State {
@Override
public void enter() {
setAvailable(true);
setTethered(false);
mTetherController.sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
mTetherController.notifyInterfaceTetheringReadiness(true, TetherInterfaceSM.this);
transitionTo(mStartingState);
break;
case CMD_INTERFACE_DOWN:
transitionTo(mUnavailableState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class StartingState extends State {
@Override
public void enter() {
setAvailable(false);
if (mUsb) {
if (!configureUsbIface(true, mIfaceName)) {
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
transitionTo(mInitialState);
return;
}
}
mTetherController.sendTetherStateChangedBroadcast();
// Skipping StartingState
transitionTo(mTetheredState);
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
// maybe a parent class?
case CMD_TETHER_UNREQUESTED:
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
case CMD_INTERFACE_DOWN:
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
transitionTo(mUnavailableState);
break;
default:
retValue = false;
}
return retValue;
}
}
class TetheredState extends State {
@Override
public void enter() {
try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
Log.e(TAG, "Error Tethering: " + e.toString());
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {
Log.e(TAG, "Error untethering after failure!" + ee.toString());
}
transitionTo(mInitialState);
return;
}
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
setAvailable(false);
setTethered(true);
mTetherController.sendTetherStateChangedBroadcast();
}
private void cleanupUpstream() {
if (mMyUpstreamIfaceName != null) {
// note that we don't care about errors here.
// sometimes interfaces are gone before we get
// to remove their rules, which generates errors.
// just do the best we can.
try {
// about to tear down NAT; gather remaining statistics
mStatsService.forceUpdate();
} catch (Exception e) {
if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
}
try {
mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
} catch (Exception e) {
if (VDBG) Log.e(
TAG, "Exception in removeInterfaceForward: " + e.toString());
}
try {
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
} catch (Exception e) {
if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
}
mMyUpstreamIfaceName = null;
}
return;
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
boolean error = false;
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
case CMD_INTERFACE_DOWN:
cleanupUpstream();
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastError(
ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
}
transitionTo(mInitialState);
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
}
if (DBG) Log.d(TAG, "Untethered " + mIfaceName);
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
(mMyUpstreamIfaceName != null &&
mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
break;
}
cleanupUpstream();
if (newUpstreamIfaceName != null) {
try {
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
mNMService.startInterfaceForwarding(mIfaceName,
newUpstreamIfaceName);
} catch (Exception e) {
Log.e(TAG, "Exception enabling Nat: " + e.toString());
try {
mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
} catch (Exception ee) {}
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
transitionTo(mInitialState);
return true;
}
}
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
error = true;
// fall through
case CMD_TETHER_MODE_DEAD:
cleanupUpstream();
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
if (error) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
}
if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
mTetherController.sendTetherStateChangedBroadcast();
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
}
transitionTo(mInitialState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class UnavailableState extends State {
@Override
public void enter() {
setAvailable(false);
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
setTethered(false);
mTetherController.sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
boolean retValue = true;
switch (message.what) {
case CMD_INTERFACE_UP:
transitionTo(mInitialState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
void setLastErrorAndTransitionToInitialState(int error) {
setLastError(error);
transitionTo(mInitialState);
}
}
/**
* A NetworkCallback class that relays information of interest to the
* tethering master state machine thread for subsequent processing.

View File

@@ -16,8 +16,6 @@
package com.android.server.connectivity.tethering;
import com.android.server.connectivity.Tethering;
/**
* @hide
*
@@ -25,5 +23,5 @@ import com.android.server.connectivity.Tethering;
*/
public interface IControlsTethering {
void sendTetherStateChangedBroadcast();
void notifyInterfaceTetheringReadiness(boolean isReady, Tethering.TetherInterfaceSM who);
void notifyInterfaceTetheringReadiness(boolean isReady, TetherInterfaceSM who);
}

View File

@@ -0,0 +1,483 @@
/*
* 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.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.IState;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import java.net.InetAddress;
/**
* @hide
*
* Tracks the eligibility of a given network interface for tethering.
*/
public class TetherInterfaceSM extends StateMachine {
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
private static final int USB_PREFIX_LENGTH = 24;
private final static String TAG = "TetherInterfaceSM";
private final static boolean DBG = false;
private final static boolean VDBG = false;
private static final Class[] messageClasses = {
TetherInterfaceSM.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
// notification from the master SM that it's not in tether mode
public static final int CMD_TETHER_MODE_DEAD = BASE_IFACE + 1;
// request from the user that it wants to tether
public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
// request from the user that it wants to untether
public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
// notification that this interface is down
public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
// notification that this interface is up
public static final int CMD_INTERFACE_UP = BASE_IFACE + 5;
// notification from the master SM that it had an error turning on cellular dun
public static final int CMD_CELL_DUN_ERROR = BASE_IFACE + 6;
// notification from the master SM that it had trouble enabling IP Forwarding
public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
// notification from the master SM that it had trouble disabling IP Forwarding
public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
// notification from the master SM that it had trouble starting tethering
public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
// notification from the master SM that it had trouble stopping tethering
public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
// notification from the master SM that it had trouble setting the DNS forwarders
public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
// the upstream connection has changed
public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12;
private final State mInitialState;
private final State mStartingState;
private final State mTetheredState;
private final State mUnavailableState;
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final IControlsTethering mTetherController;
private final boolean mUsb;
private final String mIfaceName;
private final Object mMutex; // Protects the fields below.
private boolean mAvailable;
private boolean mTethered;
private int mLastError;
private String mMyUpstreamIfaceName; // may change over time
public TetherInterfaceSM(String ifaceName, Looper looper, boolean usb, Object mutex,
INetworkManagementService nMService, INetworkStatsService statsService,
IControlsTethering tetherController) {
super(ifaceName, looper);
mNMService = nMService;
mStatsService = statsService;
mTetherController = tetherController;
mIfaceName = ifaceName;
mUsb = usb;
mMutex = mutex;
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
mInitialState = new InitialState();
addState(mInitialState);
mStartingState = new StartingState();
addState(mStartingState);
mTetheredState = new TetheredState();
addState(mTetheredState);
mUnavailableState = new UnavailableState();
addState(mUnavailableState);
setInitialState(mInitialState);
}
@Override
public String toString() {
String res = new String();
res += mIfaceName + " - ";
IState current = getCurrentState();
if (current == mInitialState) res += "InitialState";
if (current == mStartingState) res += "StartingState";
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() {
synchronized (mMutex) {
return mLastError;
}
}
private void setLastError(int error) {
synchronized (mMutex) {
mLastError = error;
if (isErrored()) {
if (mUsb) {
// note everything's been unwound by this point so nothing to do on
// further error..
configureUsbIface(false, mIfaceName);
}
}
}
}
public boolean isAvailable() {
synchronized (mMutex) {
return mAvailable;
}
}
private void setAvailable(boolean available) {
synchronized (mMutex) {
mAvailable = available;
}
}
public boolean isTethered() {
synchronized (mMutex) {
return mTethered;
}
}
private void setTethered(boolean tethered) {
synchronized (mMutex) {
mTethered = tethered;
}
}
public boolean isErrored() {
synchronized (mMutex) {
return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
}
}
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsbIface(boolean enabled, String iface) {
if (VDBG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
InterfaceConfiguration ifcg = null;
try {
ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
ifcg.setLinkAddress(new LinkAddress(addr, USB_PREFIX_LENGTH));
if (enabled) {
ifcg.setInterfaceUp();
} else {
ifcg.setInterfaceDown();
}
ifcg.clearFlag("running");
mNMService.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface, e);
return false;
}
return true;
}
private void maybeLogMessage(State state, int what) {
if (DBG) {
Log.d(TAG, state.getName() + " got " +
sMagicDecoderRing.get(what, Integer.toString(what)));
}
}
class InitialState extends State {
@Override
public void enter() {
setAvailable(true);
setTethered(false);
mTetherController.sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
mTetherController.notifyInterfaceTetheringReadiness(true, TetherInterfaceSM.this);
transitionTo(mStartingState);
break;
case CMD_INTERFACE_DOWN:
transitionTo(mUnavailableState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class StartingState extends State {
@Override
public void enter() {
setAvailable(false);
if (mUsb) {
if (!configureUsbIface(true, mIfaceName)) {
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
transitionTo(mInitialState);
return;
}
}
mTetherController.sendTetherStateChangedBroadcast();
// Skipping StartingState
transitionTo(mTetheredState);
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
// maybe a parent class?
case CMD_TETHER_UNREQUESTED:
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
case CMD_INTERFACE_DOWN:
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
transitionTo(mUnavailableState);
break;
default:
retValue = false;
}
return retValue;
}
}
class TetheredState extends State {
@Override
public void enter() {
try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
Log.e(TAG, "Error Tethering: " + e.toString());
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {
Log.e(TAG, "Error untethering after failure!" + ee.toString());
}
transitionTo(mInitialState);
return;
}
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
setAvailable(false);
setTethered(true);
mTetherController.sendTetherStateChangedBroadcast();
}
private void cleanupUpstream() {
if (mMyUpstreamIfaceName != null) {
// note that we don't care about errors here.
// sometimes interfaces are gone before we get
// to remove their rules, which generates errors.
// just do the best we can.
try {
// about to tear down NAT; gather remaining statistics
mStatsService.forceUpdate();
} catch (Exception e) {
if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
}
try {
mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
} catch (Exception e) {
if (VDBG) Log.e(
TAG, "Exception in removeInterfaceForward: " + e.toString());
}
try {
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
} catch (Exception e) {
if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
}
mMyUpstreamIfaceName = null;
}
return;
}
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
boolean retValue = true;
boolean error = false;
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
case CMD_INTERFACE_DOWN:
cleanupUpstream();
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastError(
ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
}
transitionTo(mInitialState);
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
}
if (DBG) Log.d(TAG, "Untethered " + mIfaceName);
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
(mMyUpstreamIfaceName != null &&
mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
break;
}
cleanupUpstream();
if (newUpstreamIfaceName != null) {
try {
mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
mNMService.startInterfaceForwarding(mIfaceName,
newUpstreamIfaceName);
} catch (Exception e) {
Log.e(TAG, "Exception enabling Nat: " + e.toString());
try {
mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
} catch (Exception ee) {}
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
transitionTo(mInitialState);
return true;
}
}
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
case CMD_CELL_DUN_ERROR:
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
case CMD_START_TETHERING_ERROR:
case CMD_STOP_TETHERING_ERROR:
case CMD_SET_DNS_FORWARDERS_ERROR:
error = true;
// fall through
case CMD_TETHER_MODE_DEAD:
cleanupUpstream();
try {
mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
break;
}
if (error) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
}
if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
mTetherController.sendTetherStateChangedBroadcast();
if (mUsb) {
if (!configureUsbIface(false, mIfaceName)) {
setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
}
}
transitionTo(mInitialState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class UnavailableState extends State {
@Override
public void enter() {
setAvailable(false);
setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
setTethered(false);
mTetherController.sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
boolean retValue = true;
switch (message.what) {
case CMD_INTERFACE_UP:
transitionTo(mInitialState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
void setLastErrorAndTransitionToInitialState(int error) {
setLastError(error);
transitionTo(mInitialState);
}
}