Add USB RNDIS enable/disable control

Also adding interface configuration to the tethering machine.
Also fixing netd bug that didn't send up/down portion of iface config command.
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 9aa23fe..915c5d7 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -45,10 +45,10 @@
     }
 
     private static void putAddress(StringBuffer buf, int addr) {
-        buf.append(addr  & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff);
+        buf.append((addr >> 24) & 0xff).append('.').
+            append((addr >> 16) & 0xff).append('.').
+            append((addr >> 8) & 0xff).append('.').
+            append(addr & 0xff);
     }
 
     /** Implement the Parcelable interface {@hide} */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 958d089..7c555e2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -256,14 +256,14 @@
             Log.e(TAG, "Failed to parse netmask", uhe);
             cfg.netmask = 0;
         }
-        cfg.interfaceFlags = st.nextToken("]");
+        cfg.interfaceFlags = st.nextToken("]").trim() +"]";
         Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
         return cfg;
     }
 
     public void setInterfaceConfig(
             String iface, InterfaceConfiguration cfg) throws IllegalStateException {
-        String cmd = String.format("interface setcfg %s %s %s", iface,
+        String cmd = String.format("interface setcfg %s %s %s %s", iface,
                 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
         mConnector.doCommand(cmd);
     }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 1d20074..26a2211 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.NetworkInfo;
@@ -56,6 +57,7 @@
  *
  * TODO - look for parent classes and code sharing
  */
+
 public class Tethering extends INetworkManagementEventObserver.Stub {
 
     private Notification mTetheringNotification;
@@ -80,6 +82,10 @@
 
     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.
+    private int mUsbResetExpected = 0;
+
     HierarchicalStateMachine mTetherMasterSM;
 
     public Tethering(Context context) {
@@ -113,7 +119,7 @@
                 com.android.internal.R.array.config_tether_dhcp_range);
         if (mDhcpRange.length == 0) {
             mDhcpRange = new String[2];
-            mDhcpRange[0] = new String("169.254.2.1");
+            mDhcpRange[0] = new String("169.254.2.2");
             mDhcpRange[1] = new String("169.254.2.64");
         } else if(mDhcpRange.length == 1) {
             String[] tmp = new String[2];
@@ -127,16 +133,6 @@
         mTetherableWifiRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_wifi_regexs);
 
-        String[] ifaces = new String[0];
-        try {
-            ifaces = service.listInterfaces();
-        } catch (Exception e) {
-            Log.e(TAG, "Error listing Interfaces :" + e);
-        }
-        for (String iface : ifaces) {
-            interfaceAdded(iface);
-        }
-
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
         mDnsServers[0] = "8.8.8.8";
@@ -147,6 +143,7 @@
     public void interfaceLinkStatusChanged(String iface, boolean link) {
         Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
         boolean found = false;
+        boolean usb = false;
         for (String regex : mTetherableWifiRegexs) {
             if (iface.matches(regex)) {
                 found = true;
@@ -156,6 +153,7 @@
         for (String regex: mTetherableUsbRegexs) {
             if (iface.matches(regex)) {
                 found = true;
+                usb = true;
                 break;
             }
         }
@@ -165,7 +163,7 @@
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (link) {
                 if (sm == null) {
-                    sm = new TetherInterfaceSM(iface);
+                    sm = new TetherInterfaceSM(iface, usb);
                     mIfaces.put(iface, sm);
                     sm.start();
                 }
@@ -179,7 +177,10 @@
     }
 
     public void interfaceAdded(String iface) {
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         boolean found = false;
+        boolean usb = false;
         for (String regex : mTetherableWifiRegexs) {
             if (iface.matches(regex)) {
                 found = true;
@@ -189,6 +190,7 @@
         for (String regex : mTetherableUsbRegexs) {
             if (iface.matches(regex)) {
                 found = true;
+                usb = true;
                 break;
             }
         }
@@ -196,13 +198,14 @@
             Log.d(TAG, iface + " is not a tetherable iface, ignoring");
             return;
         }
+
         synchronized (mIfaces) {
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (sm != null) {
                 Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
                 return;
             }
-            sm = new TetherInterfaceSM(iface);
+            sm = new TetherInterfaceSM(iface, usb);
             mIfaces.put(iface, sm);
             sm.start();
         }
@@ -262,6 +265,7 @@
     }
 
     private void sendTetherStateChangedBroadcast() {
+Log.e("RJGRJG", "sendTetherStateChangedBroadcast");
         IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
         IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
         try {
@@ -314,8 +318,8 @@
         if (tellUser) {
             for (Object o : availableList) {
                 String s = (String)o;
-                for (Object matchObject : mTetherableUsbRegexs) {
-                    if (s.matches((String)matchObject)) {
+                for (String match : mTetherableUsbRegexs) {
+                    if (s.matches(match)) {
                         showTetherAvailableNotification();
                         return;
                     }
@@ -414,20 +418,33 @@
         }
     }
 
-
-
-
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
-                Tethering.this.handleTtyConnect();
+                Log.w(TAG, "got UMS connected");
+                synchronized (Tethering.this) {
+                    if(mUsbResetExpected != 0) {
+                        Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
+                        mUsbResetExpected--;
+                        return;
+                    }
+                }
+                Tethering.this.toggleUsbIfaces(true); // add them
             } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
-                Tethering.this.handleTtyDisconnect();
+                Log.w(TAG, "got UMS disconneded broadcast");
+                synchronized (Tethering.this) {
+                    if(mUsbResetExpected != 0) {
+                        Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
+                        mUsbResetExpected--;
+                        return;
+                    }
+                }
+                Tethering.this.toggleUsbIfaces(false); // remove them
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+Log.e("RJGRJG", "got conn action :"+action);
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                 try {
                     NetworkInfo info = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
                     int msg;
@@ -442,6 +459,114 @@
         }
     }
 
+    // used on cable insert/remove
+    private void toggleUsbIfaces(boolean add) {
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        String[] ifaces = new String[0];
+        try {
+            ifaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return;
+        }
+        for (String iface : ifaces) {
+            for (String regex : mTetherableUsbRegexs) {
+                if (iface.matches(regex)) {
+                    if (add) {
+                        interfaceAdded(iface);
+                    } else {
+                        interfaceRemoved(iface);
+                    }
+                }
+            }
+        }
+    }
+
+    // toggled when we enter/leave the fully teathered state
+    private boolean enableRndisUsb(boolean enabled) {
+        Log.d(TAG, "enableRndisUsb(" + enabled + ")");
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        try {
+            if (enabled) {
+                // turning this on will reset USB and generate two bogus events - ignore them
+                synchronized (this) {
+                    if (!service.isUsbRNDISStarted()) {
+                        mUsbResetExpected += 2;
+                        service.startUsbRNDIS();
+                    }
+                }
+            } else {
+                if (service.isUsbRNDISStarted()) {
+                    service.stopUsbRNDIS();
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error toggling usb RNDIS :" + e);
+            return false;
+        }
+        return true;
+    }
+
+    // configured when we start tethering and unconfig'd on error or conclusion
+    private boolean configureUsb(boolean enabled) {
+        Log.d(TAG, "configureUsb(" + enabled + ")");
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        // bring toggle the interfaces
+        String[] ifaces = new String[0];
+        try {
+            ifaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return false;
+        }
+        for (String iface : ifaces) {
+            for (String regex : mTetherableUsbRegexs) {
+                if (iface.matches(regex)) {
+                    InterfaceConfiguration ifcg = null;
+                    try {
+                        ifcg = service.getInterfaceConfig(iface);
+                        if (ifcg != null) {
+                            ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
+                            ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
+                            if (enabled) {
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+                            } else {
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+                            }
+                            service.setInterfaceConfig(iface, ifcg);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+                        return false;
+                    }
+                }
+            }
+        }
+
+        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;
+    }
+
     private void handleTtyConnect() {
         Log.d(TAG, "handleTtyConnect");
         // for each of the available Tty not already supported by a ppp session,
@@ -609,6 +734,7 @@
         private HierarchicalState mUntetherInterfaceErrorState;
         private HierarchicalState mEnableNatErrorState;
         private HierarchicalState mDisableNatErrorState;
+        private HierarchicalState mUsbConfigurationErrorState;
 
         private HierarchicalState mUnavailableState;
 
@@ -617,10 +743,12 @@
         private boolean mTethered;
 
         String mIfaceName;
+        boolean mUsb;
 
-        TetherInterfaceSM(String name) {
+        TetherInterfaceSM(String name, boolean usb) {
             super(name);
             mIfaceName = name;
+            mUsb = usb;
 
             mInitialState = new InitialState();
             addState(mInitialState);
@@ -638,6 +766,8 @@
             addState(mEnableNatErrorState);
             mDisableNatErrorState = new DisableNatErrorState();
             addState(mDisableNatErrorState);
+            mUsbConfigurationErrorState = new UsbConfigurationErrorState();
+            addState(mUsbConfigurationErrorState);
             mUnavailableState = new UnavailableState();
             addState(mUnavailableState);
 
@@ -656,6 +786,7 @@
             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";
@@ -686,8 +817,15 @@
             return mErrored;
         }
 
-        private synchronized void setErrored(boolean errored) {
-            mErrored = errored;
+        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.configureUsb(false);
+            }
         }
 
         class InitialState extends HierarchicalState {
@@ -726,6 +864,19 @@
             @Override
             public void enter() {
                 setAvailable(false);
+                if (mUsb) {
+                    if (!Tethering.this.configureUsb(true)) {
+                        Message m = mTetherMasterSM.obtainMessage(
+                                TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
+                        m.obj = TetherInterfaceSM.this;
+                        mTetherMasterSM.sendMessage(m);
+
+                        m = obtainMessage(CMD_TRANSITION_TO_ERROR);
+                        m.obj = mUsbConfigurationErrorState;
+                        sendMessageAtFrontOfQueue(m);
+                        return;
+                    }
+                }
                 sendTetherStateChangedBroadcast();
             }
             @Override
@@ -739,6 +890,12 @@
                                 TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
+                        if (mUsb) {
+                            if (!Tethering.this.configureUsb(false)) {
+                                transitionTo(mUsbConfigurationErrorState);
+                                break;
+                            }
+                        }
                         transitionTo(mInitialState);
                         break;
                     case CMD_TETHER_MODE_ALIVE:
@@ -759,6 +916,10 @@
                         mTetherMasterSM.sendMessage(m);
                         transitionTo(mUnavailableState);
                         break;
+                   case CMD_TRANSITION_TO_ERROR:
+                       HierarchicalState s = (HierarchicalState)(message.obj);
+                       transitionTo(s);
+                       break;
                     default:
                         retValue = false;
                 }
@@ -788,12 +949,17 @@
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
+                if (mUsb) Tethering.this.enableRndisUsb(true);
                 Log.d(TAG, "Tethered " + mIfaceName);
                 setAvailable(false);
                 setTethered(true);
                 sendTetherStateChangedBroadcast();
             }
             @Override
+            public void exit() {
+                if(mUsb) Tethering.this.enableRndisUsb(false);
+            }
+            @Override
             public boolean processMessage(Message message) {
                 Log.d(TAG, "TetheredState.processMessage what=" + message.what);
                 boolean retValue = true;
@@ -821,6 +987,13 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
                         if (message.what == CMD_TETHER_UNREQUESTED) {
+                            if (mUsb) {
+                                if (!Tethering.this.configureUsb(false)) {
+                                    transitionTo(mUsbConfigurationErrorState);
+                                } else {
+                                    transitionTo(mUsbConfigurationErrorState);
+                                }
+                            }
                             transitionTo(mInitialState);
                         } else if (message.what == CMD_INTERFACE_DOWN) {
                             transitionTo(mUnavailableState);
@@ -857,6 +1030,12 @@
                         }
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
+                        if (mUsb) {
+                            if (!Tethering.this.configureUsb(false)) {
+                                transitionTo(mUsbConfigurationError);
+                                break;
+                            }
+                        }
                         transitionTo(mInitialState);
                         break;
                     case CMD_TRANSITION_TO_ERROR:
@@ -974,6 +1153,15 @@
                 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 {
@@ -1179,6 +1367,7 @@
         class CellDunAliveState extends HierarchicalState {
             @Override
             public void enter() {
+                Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
                 sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
             }
 
@@ -1228,6 +1417,8 @@
                         transitionTo(mInitialState);
                         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 {
@@ -1248,6 +1439,8 @@
         class TetherModeAliveState extends HierarchicalState {
             @Override
             public void enter() {
+                Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
+                sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                 for (Object o : mNotifyList) {
                     TetherInterfaceSM sm = (TetherInterfaceSM)o;
                     sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
@@ -1298,6 +1491,18 @@
                         }
                         transitionTo(mInitialState);
                         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) {
+                        }
+                        sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
+                        break;
                     default:
                        retValue = false;
                        break;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 52c8b1f..cab7b81 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -440,10 +440,11 @@
             return Phone.APN_REQUEST_FAILED;
         }
 
-        if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
+        if (DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
                 + isApnTypeActive(type) + " and state = " + state);
 
         if (!isApnTypeAvailable(type)) {
+            if (DBG) Log.d(LOG_TAG, "type not available");
             return Phone.APN_TYPE_NOT_AVAILABLE;
         }