From e3f93b02bdfde6fffd2bcbb2e1dc0785ce9f8d5a Mon Sep 17 00:00:00 2001 From: Christopher Wiley Date: Thu, 19 May 2016 11:54:54 -0700 Subject: [PATCH] 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 --- .../server/connectivity/Tethering.java | 440 +--------------- .../tethering/IControlsTethering.java | 4 +- .../tethering/TetherInterfaceSM.java | 483 ++++++++++++++++++ 3 files changed, 485 insertions(+), 442 deletions(-) create mode 100644 services/core/java/com/android/server/connectivity/tethering/TetherInterfaceSM.java diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index ac51a9e267bac..b6eec774bfe91 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -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. diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java index 5eed26cc15979..9f4effff29a82 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java +++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java @@ -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); } \ No newline at end of file diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceSM.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceSM.java new file mode 100644 index 0000000000000..e7f32a95cfad5 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceSM.java @@ -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 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); + } +}