Merge "Add error reporting for Tethering."
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c76aca1..a6668e7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -364,7 +364,7 @@
     /**
      * Sets the persisted value for enabling/disabling Mobile data.
      *
-     * @param allowMobileData Whether the mobile data connection should be
+     * @param enabled Whether the mobile data connection should be
      *            used or not.
      * @hide
      */
@@ -418,22 +418,35 @@
     /**
      * {@hide}
      */
-    public boolean tether(String iface) {
+    public String[] getTetheringErroredIfaces() {
         try {
-            return mService.tether(iface);
+            return mService.getTetheringErroredIfaces();
         } catch (RemoteException e) {
-            return false;
+            return new String[0];
         }
     }
 
     /**
+     * @return error A TETHER_ERROR value indicating success or failure type
      * {@hide}
      */
-    public boolean untether(String iface) {
+    public int tether(String iface) {
+        try {
+            return mService.tether(iface);
+        } catch (RemoteException e) {
+            return TETHER_ERROR_SERVICE_UNAVAIL;
+        }
+    }
+
+    /**
+     * @return error A TETHER_ERROR value indicating success or failure type
+     * {@hide}
+     */
+    public int untether(String iface) {
         try {
             return mService.untether(iface);
         } catch (RemoteException e) {
-            return false;
+            return TETHER_ERROR_SERVICE_UNAVAIL;
         }
     }
 
@@ -469,4 +482,41 @@
             return new String[0];
         }
     }
+
+    /** {@hide} */
+    public static final int TETHER_ERROR_NO_ERROR           = 0;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
+    /** {@hide} */
+    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
+    /** {@hide} */
+    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
+    /** {@hide} */
+    public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
+    /** {@hide} */
+    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
+    /** {@hide} */
+    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
+    /** {@hide} */
+    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
+
+    /**
+     * @param iface The name of the interface we're interested in
+     * @return error The error code of the last error tethering or untethering the named
+     *               interface
+     * {@hide}
+     */
+    public int getLastTetherError(String iface) {
+        try {
+            return mService.getLastTetherError(iface);
+        } catch (RemoteException e) {
+            return TETHER_ERROR_SERVICE_UNAVAIL;
+        }
+   }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 2514693..b05c2ed 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -55,9 +55,11 @@
 
     void setMobileDataEnabled(boolean enabled);
 
-    boolean tether(String iface);
+    int tether(String iface);
 
-    boolean untether(String iface);
+    int untether(String iface);
+
+    int getLastTetherError(String iface);
 
     boolean isTetheringSupported();
 
@@ -65,6 +67,8 @@
 
     String[] getTetheredIfaces();
 
+    String[] getTetheringErroredIfaces();
+
     String[] getTetherableUsbRegexs();
 
     String[] getTetherableWifiRegexs();
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index 5d71231..7f83b2b 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -62,6 +62,7 @@
         // determine if we advertise tethering or untethering
         ConnectivityManager cm =
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+
         mTethered = cm.getTetheredIfaces().length;
         int tetherable = cm.getTetherableIfaces().length;
         if ((mTethered == 0) && (tetherable == 0)) {
@@ -116,7 +117,7 @@
      * {@inheritDoc}
      */
     public void onClick(DialogInterface dialog, int which) {
-        boolean error =  false;
+        int error = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         if (which == POSITIVE_BUTTON) {
             ConnectivityManager cm =
@@ -130,24 +131,17 @@
                 for (String t : tetherable) {
                     for (String r : usbRegexs) {
                         if (t.matches(r)) {
-                            if (!cm.tether(t))
-                                error = true;
+                            error = cm.tether(t);
                             break;
                         }
                     }
                 }
-                if (error) {
-                    showTetheringError();
-                }
+                showTetheringError(error);
             } else {
                 for (String t : tethered) {
-                    if (!cm.untether(t)) {
-                        error = true;
-                    }
+                    error = cm.untether(t);
                 }
-                if (error) {
-                    showUnTetheringError();
-                }
+                showUnTetheringError(error);
             }
         }
         // No matter what, finish the activity
@@ -163,14 +157,23 @@
         }
     }
 
-    private void showTetheringError() {
-        Toast.makeText(this, com.android.internal.R.string.tether_error_message,
-                Toast.LENGTH_LONG).show();
+    private void showTetheringError(int error) {
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_NO_ERROR:
+            return;
+        default:
+            Toast.makeText(this, com.android.internal.R.string.tether_error_message,
+                    Toast.LENGTH_LONG).show();
+        }
     }
 
-    private void showUnTetheringError() {
-        Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
-                Toast.LENGTH_LONG).show();
+    private void showUnTetheringError(int error) {
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_NO_ERROR:
+            return;
+        default:
+            Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
+                    Toast.LENGTH_LONG).show();
+        }
     }
-
 }
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 35ea0cc..d34599f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2249,8 +2249,10 @@
     <string name="tether_button">Tether</string>
     <!-- See TETHER.   This is the button text to ignore the plugging in of the phone.. -->
     <string name="tether_button_cancel">Cancel</string>
-    <!-- See TETHER.  If there was an error mounting, this is the text. -->
-    <string name="tether_error_message">There is a problem tethering.</string>
+
+    <!-- See TETHER.  If there was a recoverable error, this is the text. -->
+    <string name="tether_error_message">We\'ve encountered a problem turning on Tethering.  Please try again.</string>
+
     <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection.  This is the title -->
     <string name="tether_available_notification_title">USB tethering available</string>
     <!-- See USB_STORAGE. This is the message. -->
@@ -2270,7 +2272,8 @@
     <string name="tether_stop_button">Disconnect</string>
     <!-- See TETHER_STOP.   This is the button text to cancel disconnecting the tether. -->
     <string name="tether_stop_button_cancel">Cancel</string>
-    <!-- See TETHER_STOP_DIALOG.  If there was an error disconnect, this is the text. -->
+
+    <!-- See TETHER_STOP.  If there was an error disconnect, this is the text. -->
     <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
 
     <!-- Strings for car mode notification -->
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 67b6200..a1c45dc 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1457,15 +1457,36 @@
     }
 
     // javadoc from interface
-    public boolean tether(String iface) {
+    public int tether(String iface) {
         enforceTetherChangePermission();
-        return isTetheringSupported() && mTethering.tether(iface);
+
+        if (isTetheringSupported()) {
+            return mTethering.tether(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
     }
 
     // javadoc from interface
-    public boolean untether(String iface) {
+    public int untether(String iface) {
         enforceTetherChangePermission();
-        return isTetheringSupported() && mTethering.untether(iface);
+
+        if (isTetheringSupported()) {
+            return mTethering.untether(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
+    }
+
+    // javadoc from interface
+    public int getLastTetherError(String iface) {
+        enforceTetherAccessPermission();
+
+        if (isTetheringSupported()) {
+            return mTethering.getLastTetherError(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
     }
 
     // TODO - proper iface API for selection by property, inspection, etc
@@ -1499,6 +1520,11 @@
         return mTethering.getTetheredIfaces();
     }
 
+    public String[] getTetheringErroredIfaces() {
+        enforceTetherAccessPermission();
+        return mTethering.getErroredIfaces();
+    }
+
     // if ro.tether.denied = true we default to no tethering
     // gservices could set the secure setting to 1 though to enable it on a build where it
     // had previously been turned off.
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 5f37a42..ee54f73 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -40,6 +40,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.util.HierarchicalState;
@@ -236,7 +237,7 @@
         }
     }
 
-    public boolean tether(String iface) {
+    public int tether(String iface) {
         Log.d(TAG, "Tethering " + iface);
         TetherInterfaceSM sm = null;
         synchronized (mIfaces) {
@@ -244,21 +245,17 @@
         }
         if (sm == null) {
             Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
         }
-        if (sm.isErrored()) {
-            Log.e(TAG, "Tried to Tether to an errored iface :" + iface + ", ignoring");
-            return false;
-        }
-        if (!sm.isAvailable()) {
+        if (!sm.isAvailable() && !sm.isErrored()) {
             Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
         }
         sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED));
-        return true;
+        return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
 
-    public boolean untether(String iface) {
+    public int untether(String iface) {
         Log.d(TAG, "Untethering " + iface);
         TetherInterfaceSM sm = null;
         synchronized (mIfaces) {
@@ -266,14 +263,26 @@
         }
         if (sm == null) {
             Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
         }
         if (sm.isErrored()) {
             Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
         }
         sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED));
-        return true;
+        return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+    }
+
+    public int getLastTetherError(String iface) {
+        TetherInterfaceSM sm = null;
+        synchronized (mIfaces) {
+            sm = mIfaces.get(iface);
+        }
+        if (sm == null) {
+            Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface + ", ignoring");
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+        }
+        return sm.getLastError();
     }
 
     private void sendTetherStateChangedBroadcast() {
@@ -430,6 +439,28 @@
         }
     }
 
+    private void showErrorToast(int error) {
+        int num;
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR:
+        case ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR:
+        case ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR:
+        case ConnectivityManager.TETHER_ERROR_MASTER_ERROR:
+            num = com.android.internal.R.string.tether_error_message;
+            break;
+        case ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR:
+        case ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR:
+            num = com.android.internal.R.string.tether_stop_error_message;
+            break;
+        default:
+            // do nothing
+            return;
+        }
+        String text = mContext.getResources().getString(num) + " - EC" + error;
+        Log.e(TAG, text);
+        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+    }
+
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
@@ -542,10 +573,9 @@
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
                             } else {
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
-                                // TODO - clean this up - maybe a better regex?
-                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
-                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
                             }
+                            ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+                            ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
                             service.setInterfaceConfig(iface, ifcg);
                         }
                     } catch (Exception e) {
@@ -611,6 +641,24 @@
         return retVal;
     }
 
+    public String[] getErroredIfaces() {
+        ArrayList<String> list = new ArrayList<String>();
+        synchronized (mIfaces) {
+            Set keys = mIfaces.keySet();
+            for (Object key : keys) {
+                TetherInterfaceSM sm = mIfaces.get(key);
+                if (sm.isErrored()) {
+                    list.add((String)key);
+                }
+            }
+        }
+        String[] retVal = new String[list.size()];
+        for (int i= 0; i< list.size(); i++) {
+            retVal[i] = list.get(i);
+        }
+        return retVal;
+    }
+
 
     class TetherInterfaceSM extends HierarchicalStateMachine {
         // notification from the master SM that it's in tether mode
@@ -637,8 +685,8 @@
         static final int CMD_STOP_TETHERING_ERROR        = 14;
         // notification from the master SM that it had trouble setting the DNS forwarders
         static final int CMD_SET_DNS_FORWARDERS_ERROR    = 15;
-        // a mechanism to transition self to error state from an enter function
-        static final int CMD_TRANSITION_TO_ERROR         = 16;
+        // a mechanism to transition self to another state from an enter function
+        static final int CMD_TRANSITION_TO_STATE         = 16;
 
         private HierarchicalState mDefaultState;
 
@@ -646,18 +694,11 @@
         private HierarchicalState mStartingState;
         private HierarchicalState mTetheredState;
 
-        private HierarchicalState mMasterTetherErrorState;
-        private HierarchicalState mTetherInterfaceErrorState;
-        private HierarchicalState mUntetherInterfaceErrorState;
-        private HierarchicalState mEnableNatErrorState;
-        private HierarchicalState mDisableNatErrorState;
-        private HierarchicalState mUsbConfigurationErrorState;
-
         private HierarchicalState mUnavailableState;
 
         private boolean mAvailable;
-        private boolean mErrored;
         private boolean mTethered;
+        int mLastError;
 
         String mIfaceName;
         boolean mUsb;
@@ -666,6 +707,7 @@
             super(name);
             mIfaceName = name;
             mUsb = usb;
+            setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
 
             mInitialState = new InitialState();
             addState(mInitialState);
@@ -673,18 +715,6 @@
             addState(mStartingState);
             mTetheredState = new TetheredState();
             addState(mTetheredState);
-            mMasterTetherErrorState = new MasterTetherErrorState();
-            addState(mMasterTetherErrorState);
-            mTetherInterfaceErrorState = new TetherInterfaceErrorState();
-            addState(mTetherInterfaceErrorState);
-            mUntetherInterfaceErrorState = new UntetherInterfaceErrorState();
-            addState(mUntetherInterfaceErrorState);
-            mEnableNatErrorState = new EnableNatErrorState();
-            addState(mEnableNatErrorState);
-            mDisableNatErrorState = new DisableNatErrorState();
-            addState(mDisableNatErrorState);
-            mUsbConfigurationErrorState = new UsbConfigurationErrorState();
-            addState(mUsbConfigurationErrorState);
             mUnavailableState = new UnavailableState();
             addState(mUnavailableState);
 
@@ -698,19 +728,30 @@
             if (current == mInitialState) res += "InitialState";
             if (current == mStartingState) res += "StartingState";
             if (current == mTetheredState) res += "TetheredState";
-            if (current == mMasterTetherErrorState) res += "MasterTetherErrorState";
-            if (current == mTetherInterfaceErrorState) res += "TetherInterfaceErrorState";
-            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";
-            if (mErrored) res += " - ERRORED";
+            res += " - lastError =" + mLastError;
             return res;
         }
 
+        public synchronized int getLastError() {
+            return mLastError;
+        }
+
+        private synchronized void setLastError(int error) {
+            mLastError = error;
+
+            if (isErrored()) {
+                if (mUsb) {
+                    // note everything's been unwound by this point so nothing to do on
+                    // further error..
+                    Tethering.this.configureUsbIface(false);
+                }
+                Tethering.this.showErrorToast(error);
+            }
+        }
+
         // synchronized between this getter and the following setter
         public synchronized boolean isAvailable() {
             return mAvailable;
@@ -731,18 +772,7 @@
 
         // synchronized between this getter and the following setter
         public synchronized boolean isErrored() {
-            return mErrored;
-        }
-
-        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.configureUsbIface(false);
-            }
+            return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
         }
 
         class InitialState extends HierarchicalState {
@@ -750,7 +780,6 @@
             public void enter() {
                 setAvailable(true);
                 setTethered(false);
-                setErrored(false);
                 sendTetherStateChangedBroadcast();
             }
 
@@ -760,6 +789,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_REQUESTED:
+                        setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                         Message m = mTetherMasterSM.obtainMessage(
                                 TetherMasterSM.CMD_TETHER_MODE_REQUESTED);
                         m.obj = TetherInterfaceSM.this;
@@ -788,8 +818,10 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
 
-                        m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                        m.obj = mUsbConfigurationErrorState;
+                        setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
+
+                        m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                        m.obj = mInitialState;
                         sendMessageAtFrontOfQueue(m);
                         return;
                     }
@@ -809,7 +841,8 @@
                         mTetherMasterSM.sendMessage(m);
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
-                                transitionTo(mUsbConfigurationErrorState);
+                                setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                 break;
                             }
                         }
@@ -824,7 +857,8 @@
                     case CMD_START_TETHERING_ERROR:
                     case CMD_STOP_TETHERING_ERROR:
                     case CMD_SET_DNS_FORWARDERS_ERROR:
-                        transitionTo(mMasterTetherErrorState);
+                        setLastErrorAndTransitionToInitialState(
+                                ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                         break;
                     case CMD_INTERFACE_DOWN:
                         m = mTetherMasterSM.obtainMessage(
@@ -833,7 +867,7 @@
                         mTetherMasterSM.sendMessage(m);
                         transitionTo(mUnavailableState);
                         break;
-                   case CMD_TRANSITION_TO_ERROR:
+                   case CMD_TRANSITION_TO_STATE:
                        HierarchicalState s = (HierarchicalState)(message.obj);
                        transitionTo(s);
                        break;
@@ -853,16 +887,24 @@
                 try {
                     service.tetherInterface(mIfaceName);
                 } catch (Exception e) {
-                    Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                    m.obj = mTetherInterfaceErrorState;
+                    setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
+
+                    Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                    m.obj = mInitialState;
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
                 try {
                     service.enableNat(mIfaceName, mUpstreamIfaceName);
                 } catch (Exception e) {
-                    Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                    m.obj = mEnableNatErrorState;
+                    try {
+                        service.untetherInterface(mIfaceName);
+                    } catch (Exception ee) {}
+
+                    setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
+
+                    Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                    m.obj = mInitialState;
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
@@ -890,13 +932,19 @@
                         try {
                             service.disableNat(mIfaceName, mUpstreamIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mDisableNatErrorState);
+                            try {
+                                service.untetherInterface(mIfaceName);
+                            } catch (Exception ee) {}
+
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
                             break;
                         }
                         try {
                             service.untetherInterface(mIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mUntetherInterfaceErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                             break;
                         }
                         Message m = mTetherMasterSM.obtainMessage(
@@ -906,13 +954,11 @@
                         if (message.what == CMD_TETHER_UNREQUESTED) {
                             if (mUsb) {
                                 if (!Tethering.this.configureUsbIface(false)) {
-                                    transitionTo(mUsbConfigurationErrorState);
-                                } else {
-                                    transitionTo(mInitialState);
+                                    setLastError(
+                                            ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                 }
-                            } else {
-                                transitionTo(mInitialState);
                             }
+                            transitionTo(mInitialState);
                         } else if (message.what == CMD_INTERFACE_DOWN) {
                             transitionTo(mUnavailableState);
                         }
@@ -932,30 +978,36 @@
                         try {
                             service.disableNat(mIfaceName, mUpstreamIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mDisableNatErrorState);
+                            try {
+                                service.untetherInterface(mIfaceName);
+                            } catch (Exception ee) {}
+
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
                             break;
                         }
                         try {
                             service.untetherInterface(mIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mUntetherInterfaceErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                             break;
                         }
                         if (error) {
-                            transitionTo(mMasterTetherErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                             break;
                         }
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
-                                transitionTo(mUsbConfigurationErrorState);
-                                break;
+                                setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                             }
                         }
                         transitionTo(mInitialState);
                         break;
-                    case CMD_TRANSITION_TO_ERROR:
+                    case CMD_TRANSITION_TO_STATE:
                         HierarchicalState s = (HierarchicalState)(message.obj);
                         transitionTo(s);
                         break;
@@ -971,7 +1023,7 @@
             @Override
             public void enter() {
                 setAvailable(false);
-                setErrored(false);
+                setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                 setTethered(false);
                 sendTetherStateChangedBroadcast();
             }
@@ -990,95 +1042,11 @@
             }
         }
 
-
-        class ErrorState extends HierarchicalState {
-            int mErrorNotification;
-            @Override
-            public boolean processMessage(Message message) {
-                boolean retValue = true;
-                switch (message.what) {
-                    case CMD_TETHER_REQUESTED:
-                        sendTetherStateChangedBroadcast();
-                        break;
-                    default:
-                        retValue = false;
-                        break;
-                }
-                return retValue;
-            }
+        void setLastErrorAndTransitionToInitialState(int error) {
+            setLastError(error);
+            transitionTo(mInitialState);
         }
 
-        class MasterTetherErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error in Master Tether state " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class TetherInterfaceErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to tether " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class UntetherInterfaceErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to untether " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class EnableNatErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to enable NAT " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-                try {
-                    service.untetherInterface(mIfaceName);
-                } catch (Exception e) {}
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-
-        class DisableNatErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to disable NAT " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-                try {
-                    service.untetherInterface(mIfaceName);
-                } catch (Exception e) {}
-                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 {