Extract TetherInterfaceSM to its own class.
( cherry-pick of e3f93b02bdfde6fffd2bcbb2e1dc0785ce9f8d5a )
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
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index ac51a9e..b6eec77 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.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 @@
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
@@ -981,442 +979,6 @@
}
/**
- * @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 5eed26c..9f4efff 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 @@
*/
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 0000000..e7f32a9
--- /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<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);
+ }
+}