Tethering improvements.

Changes due to comments.
Fixing Tether teardown.

bug:2422545
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index a48ccf9..5d71231 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -141,7 +141,7 @@
                 }
             } else {
                 for (String t : tethered) {
-                    if (!cm.untether("ppp0")) {
+                    if (!cm.untether(t)) {
                         error = true;
                     }
                 }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 10d6e3a..16638bc 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -76,14 +76,21 @@
 
     private BroadcastReceiver mStateReceiver;
 
+    private static final String USB_NEAR_IFACE_ADDR      = "169.254.2.1";
+
     private String[] mDhcpRange;
+    private static final String DHCP_DEFAULT_RANGE_START = "169.254.2.10";
+    private static final String DHCP_DEFAULT_RANGE_STOP  = "169.254.2.64";
 
     private String[] mDnsServers;
+    private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
+    private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
 
     private String mUpstreamIfaceName;
 
     // turning on/off RNDIS resets the interface generating and extra discon/conn cycle
     // count how many to ignore..  Self correcting if you plug/unplug a bunch of times.
+    // TODO - brittle - maybe don't need?
     private int mUsbResetExpected = 0;
 
     HierarchicalStateMachine mTetherMasterSM;
@@ -119,8 +126,8 @@
                 com.android.internal.R.array.config_tether_dhcp_range);
         if (mDhcpRange.length == 0) {
             mDhcpRange = new String[2];
-            mDhcpRange[0] = new String("169.254.2.2");
-            mDhcpRange[1] = new String("169.254.2.64");
+            mDhcpRange[0] = DHCP_DEFAULT_RANGE_START;
+            mDhcpRange[1] = DHCP_DEFAULT_RANGE_STOP;
         } else if(mDhcpRange.length == 1) {
             String[] tmp = new String[2];
             tmp[0] = mDhcpRange[0];
@@ -135,8 +142,8 @@
 
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
-        mDnsServers[0] = "8.8.8.8";
-        mDnsServers[1] = "4.2.2.2";
+        mDnsServers[0] = DNS_DEFAULT_SERVER1;
+        mDnsServers[1] = DNS_DEFAULT_SERVER2;
         mUpstreamIfaceName = "rmnet0";
     }
 
@@ -300,7 +307,8 @@
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
                 erroredList);
         mContext.sendStickyBroadcast(broadcast);
-
+        Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
+                activeList.size() + ", " + erroredList.size());
         // check if we need to send a USB notification
         // Check if the user wants to be bothered
         boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
@@ -429,7 +437,7 @@
                         return;
                     }
                 }
-                Tethering.this.toggleUsbIfaces(true); // add them
+                Tethering.this.enableUsbIfaces(true); // add them
             } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
                 Log.w(TAG, "got UMS disconneded broadcast");
                 synchronized (Tethering.this) {
@@ -439,7 +447,7 @@
                         return;
                     }
                 }
-                Tethering.this.toggleUsbIfaces(false); // remove them
+                Tethering.this.enableUsbIfaces(false); // remove them
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
@@ -458,7 +466,7 @@
     }
 
     // used on cable insert/remove
-    private void toggleUsbIfaces(boolean add) {
+    private void enableUsbIfaces(boolean enable) {
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         String[] ifaces = new String[0];
@@ -471,7 +479,7 @@
         for (String iface : ifaces) {
             for (String regex : mTetherableUsbRegexs) {
                 if (iface.matches(regex)) {
-                    if (add) {
+                    if (enable) {
                         interfaceAdded(iface);
                     } else {
                         interfaceRemoved(iface);
@@ -482,8 +490,8 @@
     }
 
     // toggled when we enter/leave the fully teathered state
-    private boolean enableRndisUsb(boolean enabled) {
-        Log.d(TAG, "enableRndisUsb(" + enabled + ")");
+    private boolean enableUsbRndis(boolean enabled) {
+        Log.d(TAG, "enableUsbRndis(" + enabled + ")");
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
                 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
 
@@ -509,8 +517,8 @@
     }
 
     // configured when we start tethering and unconfig'd on error or conclusion
-    private boolean configureUsb(boolean enabled) {
-        Log.d(TAG, "configureUsb(" + enabled + ")");
+    private boolean configureUsbIface(boolean enabled) {
+        Log.d(TAG, "configureUsbIface(" + enabled + ")");
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -536,6 +544,10 @@
                                 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("running ","");
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running","");
                             }
                             service.setInterfaceConfig(iface, ifcg);
                         }
@@ -547,21 +559,6 @@
             }
         }
 
-        if (!enabled) {
-            // turn off ndis
-            try {
-                synchronized (this) {
-                    if (service.isUsbRNDISStarted()) {
-                        mUsbResetExpected += 2;
-                        service.stopUsbRNDIS();
-                    }
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Error stopping usb RNDIS :" + e);
-                return false;
-            }
-        }
-
         return true;
     }
 
@@ -822,7 +819,7 @@
             if (errored && mUsb) {
                 // note everything's been unwound by this point so nothing to do on
                 // further error..
-                Tethering.this.configureUsb(false);
+                Tethering.this.configureUsbIface(false);
             }
         }
 
@@ -863,7 +860,7 @@
             public void enter() {
                 setAvailable(false);
                 if (mUsb) {
-                    if (!Tethering.this.configureUsb(true)) {
+                    if (!Tethering.this.configureUsbIface(true)) {
                         Message m = mTetherMasterSM.obtainMessage(
                                 TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
                         m.obj = TetherInterfaceSM.this;
@@ -889,7 +886,7 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
                         if (mUsb) {
-                            if (!Tethering.this.configureUsb(false)) {
+                            if (!Tethering.this.configureUsbIface(false)) {
                                 transitionTo(mUsbConfigurationErrorState);
                                 break;
                             }
@@ -947,7 +944,7 @@
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
-                if (mUsb) Tethering.this.enableRndisUsb(true);
+                if (mUsb) Tethering.this.enableUsbRndis(true);
                 Log.d(TAG, "Tethered " + mIfaceName);
                 setAvailable(false);
                 setTethered(true);
@@ -955,7 +952,7 @@
             }
             @Override
             public void exit() {
-                if(mUsb) Tethering.this.enableRndisUsb(false);
+                if (mUsb) Tethering.this.enableUsbRndis(false);
             }
             @Override
             public boolean processMessage(Message message) {
@@ -986,7 +983,7 @@
                         mTetherMasterSM.sendMessage(m);
                         if (message.what == CMD_TETHER_UNREQUESTED) {
                             if (mUsb) {
-                                if (!Tethering.this.configureUsb(false)) {
+                                if (!Tethering.this.configureUsbIface(false)) {
                                     transitionTo(mUsbConfigurationErrorState);
                                 } else {
                                     transitionTo(mInitialState);
@@ -998,7 +995,6 @@
                             transitionTo(mUnavailableState);
                         }
                         Log.d(TAG, "Untethered " + mIfaceName);
-                        sendTetherStateChangedBroadcast();
                         break;
                     case CMD_CELL_DUN_ERROR:
                     case CMD_IP_FORWARDING_ENABLE_ERROR:
@@ -1030,7 +1026,7 @@
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
-                            if (!Tethering.this.configureUsb(false)) {
+                            if (!Tethering.this.configureUsbIface(false)) {
                                 transitionTo(mUsbConfigurationErrorState);
                                 break;
                             }
@@ -1187,7 +1183,6 @@
         private HierarchicalState mCellDunRequestedState;
         private HierarchicalState mCellDunAliveState;
         private HierarchicalState mTetherModeAliveState;
-        private HierarchicalState mCellDunUnRequestedState;
 
         private HierarchicalState mCellDunErrorState;
         private HierarchicalState mSetIpForwardingEnabledErrorState;
@@ -1215,8 +1210,6 @@
             addState(mCellDunAliveState);
             mTetherModeAliveState = new TetherModeAliveState();
             addState(mTetherModeAliveState);
-            mCellDunUnRequestedState = new CellDunUnRequestedState();
-            addState(mCellDunUnRequestedState);
 
             mCellDunErrorState = new CellDunErrorState();
             addState(mCellDunErrorState);
@@ -1236,8 +1229,81 @@
             setInitialState(mInitialState);
         }
 
+        class TetherMasterUtilState extends HierarchicalState {
+            @Override
+            public boolean processMessage(Message m) {
+                return false;
+            }
+            public int turnOnMobileDun() {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                int retValue = Phone.APN_REQUEST_FAILED;
+                try {
+                    retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                            Phone.FEATURE_ENABLE_DUN, new Binder());
+                } catch (Exception e) {
+                }
+                return retValue;
+            }
+            public boolean turnOffMobileDun() {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service =
+                        IConnectivityManager.Stub.asInterface(b);
+                try {
+                    service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                            Phone.FEATURE_ENABLE_DUN);
+                } catch (Exception e) {
+                    return false;
+                }
+                return true;
+            }
+            public boolean turnOnMasterTetherSettings() {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service =
+                        INetworkManagementService.Stub.asInterface(b);
+                try {
+                    service.setIpForwardingEnabled(true);
+                } catch (Exception e) {
+                    transitionTo(mSetIpForwardingEnabledErrorState);
+                    return false;
+                }
+                try {
+                    service.startTethering(mDhcpRange[0], mDhcpRange[1]);
+                } catch (Exception e) {
+                    transitionTo(mStartTetheringErrorState);
+                    return false;
+                }
+                try {
+                    service.setDnsForwarders(mDnsServers);
+                } catch (Exception e) {
+                    transitionTo(mSetDnsForwardersErrorState);
+                    return false;
+                }
+                transitionTo(mTetherModeAliveState);
+                return true;
+            }
+            public boolean turnOffMasterTetherSettings() {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service =
+                        INetworkManagementService.Stub.asInterface(b);
+                try {
+                    service.stopTethering();
+                } catch (Exception e) {
+                    transitionTo(mStopTetheringErrorState);
+                    return false;
+                }
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception e) {
+                    transitionTo(mSetIpForwardingDisabledErrorState);
+                    return false;
+                }
+                transitionTo(mInitialState);
+                return true;
+            }
+        }
 
-        class InitialState extends HierarchicalState {
+        class InitialState extends TetherMasterUtilState {
             @Override
             public boolean processMessage(Message message) {
                 Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
@@ -1267,20 +1333,11 @@
                 return retValue;
             }
         }
-        class CellDunRequestedState extends HierarchicalState {
+        class CellDunRequestedState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 ++mSequenceNumber;
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
-                int result;
-                try {
-                    result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            Phone.FEATURE_ENABLE_DUN, new Binder());
-                } catch (Exception e) {
-                    result = Phone.APN_REQUEST_FAILED;
-                }
+                int result = turnOnMobileDun();
                 switch (result) {
                     case Phone.APN_ALREADY_ACTIVE:
                         Log.d(TAG, "Dun already active");
@@ -1319,34 +1376,13 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                transitionTo(mInitialState);
                             }
                         }
                         break;
                     case CMD_CELL_DUN_ENABLED:
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-
-                        try {
-                            service.setIpForwardingEnabled(true);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingEnabledErrorState);
-                            break;
-                        }
-                        try {
-                            service.startTethering(mDhcpRange[0], mDhcpRange[1]);
-                        } catch (Exception e) {
-                            transitionTo(mStartTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setDnsForwarders(mDnsServers);
-                        } catch (Exception e) {
-                            transitionTo(mSetDnsForwardersErrorState);
-                            break;
-                        }
-                        transitionTo(mTetherModeAliveState);
+                        turnOnMasterTetherSettings();
                         break;
                     case CMD_CELL_DUN_DISABLED:
                         break;
@@ -1363,7 +1399,7 @@
             }
         }
 
-        class CellDunAliveState extends HierarchicalState {
+        class CellDunAliveState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
@@ -1378,29 +1414,7 @@
                     case CMD_TETHER_MODE_REQUESTED:
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
                         mNotifyList.add(who);
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-
-                        try {
-                            service.setIpForwardingEnabled(true);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingEnabledErrorState);
-                            break;
-                        }
-                        try {
-                            service.startTethering(mDhcpRange[0], mDhcpRange[1]);
-                        } catch (Exception e) {
-                            transitionTo(mStartTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setDnsForwarders(mDnsServers);
-                        } catch (Exception e) {
-                            transitionTo(mSetDnsForwardersErrorState);
-                            break;
-                        }
-                        transitionTo(mTetherModeAliveState);
+                        turnOnMasterTetherSettings();
                         break;
                     case CMD_TETHER_MODE_UNREQUESTED:
                         who = (TetherInterfaceSM)message.obj;
@@ -1408,7 +1422,8 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                transitionTo(mInitialState);
                             }
                         }
                         break;
@@ -1418,13 +1433,7 @@
                     case CMD_CELL_DUN_RENEW:
                         Log.d(TAG, "renewing dun connection - requeuing for another " +
                                 CELL_DUN_RENEW_MS + "ms");
-                        b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                        IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
-                        try {
-                            cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                    Phone.FEATURE_ENABLE_DUN, new Binder());
-                        } catch (Exception e) {
-                        }
+                        turnOnMobileDun();
                         sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                         break;
                     default:
@@ -1435,7 +1444,7 @@
             }
         }
 
-        class TetherModeAliveState extends HierarchicalState {
+        class TetherModeAliveState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
@@ -1461,7 +1470,8 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                turnOffMasterTetherSettings(); // transitions appropriately
                             }
                         }
                         break;
@@ -1473,33 +1483,12 @@
                             sm.sendMessage(sm.obtainMessage(
                                     TetherInterfaceSM.CMD_TETHER_MODE_DEAD));
                         }
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-                        try {
-                            service.stopTethering();
-                        } catch (Exception e) {
-                            transitionTo(mStopTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setIpForwardingEnabled(false);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingDisabledErrorState);
-                            break;
-                        }
-                        transitionTo(mInitialState);
+                        turnOffMasterTetherSettings(); // transitions appropriately
                         break;
                     case CMD_CELL_DUN_RENEW:
                         Log.d(TAG, "renewing dun connection - requeuing for another " +
                                 CELL_DUN_RENEW_MS + "ms");
-                        b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                        IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
-                        try {
-                            cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                    Phone.FEATURE_ENABLE_DUN, new Binder());
-                        } catch (Exception e) {
-                        }
+                        turnOnMobileDun();
                         sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                         break;
                     default:
@@ -1510,58 +1499,6 @@
             }
         }
 
-        class CellDunUnRequestedState extends HierarchicalState {
-            @Override
-            public void enter() {
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
-                NetworkInfo dunInfo = null;
-                try {
-                    dunInfo = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
-                } catch (Exception e) {}
-                if (dunInfo != null && !dunInfo.isConnectedOrConnecting()) {
-                    sendMessage(obtainMessage(CMD_CELL_DUN_DISABLED));
-                    return;
-                }
-                try {
-                    service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            Phone.FEATURE_ENABLE_DUN);
-                } catch (Exception e) {}
-                Message m =  obtainMessage(CMD_CELL_DUN_TIMEOUT);
-                m.arg1 = ++mSequenceNumber;
-                // use a short timeout - this will often be a no-op and
-                // we just want this request to get into the queue before we
-                // try again.
-                sendMessageDelayed(m, CELL_DISABLE_DUN_TIMEOUT_MS);
-            }
-            @Override
-            public boolean processMessage(Message message) {
-                Log.d(TAG, "CellDunUnRequestedState.processMessage what=" + message.what);
-                boolean retValue = true;
-                switch (message.what) {
-                    case CMD_TETHER_MODE_REQUESTED:
-                    case CMD_TETHER_MODE_UNREQUESTED:
-                        deferMessage(message);
-                        break;
-                    case CMD_CELL_DUN_DISABLED:
-                        transitionTo(mInitialState);
-                        break;
-                    case CMD_CELL_DUN_TIMEOUT:
-                        // if we aren't using a sep apn, we won't get a disconnect broadcast..
-                        // just go back to initial after our short pause
-                        if (message.arg1 == mSequenceNumber) {
-                            transitionTo(mInitialState);
-                        }
-                        break;
-                    default:
-                        retValue = false;
-                        break;
-                }
-                return retValue;
-            }
-        }
-
         class ErrorState extends HierarchicalState {
             int mErrorNotification;
             @Override