OSDN Git Service

DO NOT MERGE Add DhcpStateMachine
authorIrfan Sheriff <isheriff@google.com>
Fri, 6 May 2011 23:07:58 +0000 (16:07 -0700)
committerIrfan Sheriff <isheriff@google.com>
Thu, 12 May 2011 21:10:11 +0000 (14:10 -0700)
Add DhcpStateMachine for interation with dhcpcd

- Supports wakeup and renewal on dhcp
- Supports multiple controllers to use the state machine
  simultaneously
- Optionally, a controller can request a notification prior
    to DHCP request/renewal being sent

Change-Id: I5324814b19ff19863aa6fa89f1e3f0a202930c98

core/java/android/net/DhcpStateMachine.java [new file with mode: 0644]
core/java/android/net/NetworkUtils.java
core/jni/android_net_NetUtils.cpp
wifi/java/android/net/wifi/WifiStateMachine.java

diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
new file mode 100644 (file)
index 0000000..f5cf14b
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2011 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 android.net;
+
+import com.android.internal.util.Protocol;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.DhcpInfoInternal;
+import android.net.NetworkUtils;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * StateMachine that interacts with the native DHCP client and can talk to
+ * a controller that also needs to be a StateMachine
+ *
+ * The Dhcp state machine provides the following features:
+ * - Wakeup and renewal using the native DHCP client  (which will not renew
+ *   on its own when the device is in suspend state and this can lead to device
+ *   holding IP address beyond expiry)
+ * - A notification right before DHCP request or renewal is started. This
+ *   can be used for any additional setup before DHCP. For example, wifi sets
+ *   BT-Wifi coex settings right before DHCP is initiated
+ *
+ * @hide
+ */
+public class DhcpStateMachine extends HierarchicalStateMachine {
+
+    private static final String TAG = "DhcpStateMachine";
+    private static final boolean DBG = false;
+
+
+    /* A StateMachine that controls the DhcpStateMachine */
+    private HierarchicalStateMachine mController;
+
+    private Context mContext;
+    private BroadcastReceiver mBroadcastReceiver;
+    private AlarmManager mAlarmManager;
+    private PendingIntent mDhcpRenewalIntent;
+    private PowerManager.WakeLock mDhcpRenewWakeLock;
+    private static final String WAKELOCK_TAG = "DHCP";
+
+    private static final int DHCP_RENEW = 0;
+    private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
+
+    private enum DhcpAction {
+        START,
+        RENEW
+    };
+
+    private String mInterfaceName;
+    private boolean mRegisteredForPreDhcpNotification = false;
+
+    private static final int BASE = Protocol.BASE_DHCP;
+
+    /* Commands from controller to start/stop DHCP */
+    public static final int CMD_START_DHCP                  = BASE + 1;
+    public static final int CMD_STOP_DHCP                   = BASE + 2;
+    public static final int CMD_RENEW_DHCP                  = BASE + 3;
+
+    /* Notification from DHCP state machine prior to DHCP discovery/renewal */
+    public static final int CMD_PRE_DHCP_ACTION             = BASE + 4;
+    /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
+     * success/failure */
+    public static final int CMD_POST_DHCP_ACTION            = BASE + 5;
+
+    /* Command from controller to indicate DHCP discovery/renewal can continue
+     * after pre DHCP action is complete */
+    public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 6;
+
+    /* Message.arg1 arguments to CMD_POST_DHCP notification */
+    public static final int DHCP_SUCCESS = 1;
+    public static final int DHCP_FAILURE = 2;
+
+    private HierarchicalState mDefaultState = new DefaultState();
+    private HierarchicalState mStoppedState = new StoppedState();
+    private HierarchicalState mWaitBeforeStartState = new WaitBeforeStartState();
+    private HierarchicalState mRunningState = new RunningState();
+    private HierarchicalState mWaitBeforeRenewalState = new WaitBeforeRenewalState();
+
+    private DhcpStateMachine(Context context, HierarchicalStateMachine controller, String intf) {
+        super(TAG);
+
+        mContext = context;
+        mController = controller;
+        mInterfaceName = intf;
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null);
+        mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0);
+
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                //DHCP renew
+                if (DBG) Log.d(TAG, "Sending a DHCP renewal " + this);
+                //acquire a 40s wakelock to finish DHCP renewal
+                mDhcpRenewWakeLock.acquire(40000);
+                sendMessage(CMD_RENEW_DHCP);
+            }
+        };
+        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_DHCP_RENEW));
+
+        addState(mDefaultState);
+            addState(mStoppedState, mDefaultState);
+            addState(mWaitBeforeStartState, mDefaultState);
+            addState(mRunningState, mDefaultState);
+            addState(mWaitBeforeRenewalState, mDefaultState);
+
+        setInitialState(mStoppedState);
+    }
+
+    public static DhcpStateMachine makeDhcpStateMachine(Context context, HierarchicalStateMachine controller,
+            String intf) {
+        DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
+        dsm.start();
+        return dsm;
+    }
+
+    /**
+     * This sends a notification right before DHCP request/renewal so that the
+     * controller can do certain actions before DHCP packets are sent out.
+     * When the controller is ready, it sends a CMD_PRE_DHCP_ACTION_COMPLETE message
+     * to indicate DHCP can continue
+     *
+     * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
+     * handling during Dhcp
+     */
+    public void registerForPreDhcpNotification() {
+        mRegisteredForPreDhcpNotification = true;
+    }
+
+    class DefaultState extends HierarchicalState {
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_RENEW_DHCP:
+                    Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
+                    break;
+                case HSM_QUIT_CMD:
+                    mContext.unregisterReceiver(mBroadcastReceiver);
+                    //let parent kill the state machine
+                    return NOT_HANDLED;
+                default:
+                    Log.e(TAG, "Error! unhandled message  " + message);
+                    break;
+            }
+            return HANDLED;
+        }
+    }
+
+
+    class StoppedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_START_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        /* Notify controller before starting DHCP */
+                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
+                        transitionTo(mWaitBeforeStartState);
+                    } else {
+                        if (runDhcp(DhcpAction.START)) {
+                            transitionTo(mRunningState);
+                        }
+                    }
+                    break;
+                case CMD_STOP_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    class WaitBeforeStartState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
+                    if (runDhcp(DhcpAction.START)) {
+                        transitionTo(mRunningState);
+                    } else {
+                        transitionTo(mStoppedState);
+                    }
+                    break;
+                case CMD_STOP_DHCP:
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    class RunningState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_STOP_DHCP:
+                    mAlarmManager.cancel(mDhcpRenewalIntent);
+                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+                    }
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_RENEW_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        /* Notify controller before starting DHCP */
+                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
+                        transitionTo(mWaitBeforeRenewalState);
+                    } else {
+                        if (!runDhcp(DhcpAction.RENEW)) {
+                            transitionTo(mStoppedState);
+                        }
+                    }
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+            }
+            return retValue;
+        }
+    }
+
+    class WaitBeforeRenewalState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_STOP_DHCP:
+                    mAlarmManager.cancel(mDhcpRenewalIntent);
+                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+                    }
+                    transitionTo(mStoppedState);
+                    break;
+                case CMD_PRE_DHCP_ACTION_COMPLETE:
+                    if (runDhcp(DhcpAction.RENEW)) {
+                       transitionTo(mRunningState);
+                    } else {
+                       transitionTo(mStoppedState);
+                    }
+                    break;
+                case CMD_START_DHCP:
+                    //ignore
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+    }
+
+    private boolean runDhcp(DhcpAction dhcpAction) {
+        boolean success = false;
+        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+
+        if (dhcpAction == DhcpAction.START) {
+            Log.d(TAG, "DHCP request on " + mInterfaceName);
+            success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
+        } else if (dhcpAction == DhcpAction.RENEW) {
+            Log.d(TAG, "DHCP renewal on " + mInterfaceName);
+            success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
+        }
+
+        if (success) {
+            Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
+            //Do it a bit earlier than half the lease duration time
+            //to beat the native DHCP client and avoid extra packets
+            //48% for one hour lease time = 29 minutes
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() +
+                    dhcpInfoInternal.leaseDuration * 480, //in milliseconds
+                    mDhcpRenewalIntent);
+
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
+                .sendToTarget();
+        } else {
+            Log.d(TAG, "DHCP failed on " + mInterfaceName + ": " +
+                    NetworkUtils.getDhcpError());
+            NetworkUtils.stopDhcp(mInterfaceName);
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
+                .sendToTarget();
+        }
+        return success;
+    }
+}
index b3f3988..823d10f 100644 (file)
@@ -80,6 +80,16 @@ public class NetworkUtils {
     public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo);
 
     /**
+     * Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
+     * a result (either success or failure) from the daemon.
+     * @param interfaceName the name of the interface to configure
+     * @param ipInfo if the request succeeds, this object is filled in with
+     * the IP address information.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo);
+
+    /**
      * Shut down the DHCP client daemon.
      * @param interfaceName the name of the interface for which the daemon
      * should be stopped
index 3adf770..4becad7 100644 (file)
@@ -40,6 +40,16 @@ int dhcp_do_request(const char *ifname,
                     const char *dns2,
                     const char *server,
                     uint32_t  *lease);
+
+int dhcp_do_request_renew(const char *ifname,
+                    const char *ipaddr,
+                    const char *gateway,
+                    uint32_t  *prefixLength,
+                    const char *dns1,
+                    const char *dns2,
+                    const char *server,
+                    uint32_t  *lease);
+
 int dhcp_stop(const char *ifname);
 int dhcp_release_lease(const char *ifname);
 char *dhcp_get_errmsg();
@@ -146,7 +156,8 @@ static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstri
     return (jint)result;
 }
 
-static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
+        jobject info, bool renew)
 {
     int result;
     char  ipaddr[PROPERTY_VALUE_MAX];
@@ -160,8 +171,14 @@ static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring if
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
     if (nameStr == NULL) return (jboolean)false;
 
-    result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
-                                        dns1, dns2, server, &lease);
+    if (renew) {
+        result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
+    } else {
+        result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
+                dns1, dns2, server, &lease);
+    }
+
     env->ReleaseStringUTFChars(ifname, nameStr);
     if (result == 0 && dhcpInfoInternalFieldIds.dhcpInfoInternalClass != NULL) {
         env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
@@ -176,6 +193,17 @@ static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring if
     return (jboolean)(result == 0);
 }
 
+static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
+}
+
+static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+{
+    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
+}
+
+
 static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
     int result;
@@ -219,6 +247,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
     { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
     { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcp },
+    { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z",  (void *)android_net_utils_runDhcpRenew },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
index 75ca2b7..a87ee9a 100644 (file)
@@ -48,6 +48,7 @@ import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpInfoInternal;
+import android.net.DhcpStateMachine;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -152,6 +153,7 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     private NetworkInfo mNetworkInfo;
     private SupplicantStateTracker mSupplicantStateTracker;
     private WpsStateMachine mWpsStateMachine;
+    private DhcpStateMachine mDhcpStateMachine;
 
     private AlarmManager mAlarmManager;
     private PendingIntent mScanIntent;
@@ -189,10 +191,10 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     static final int CMD_START_DRIVER                     = BASE + 13;
     /* Start the driver */
     static final int CMD_STOP_DRIVER                      = BASE + 14;
-    /* Indicates DHCP succeded */
-    static final int CMD_IP_CONFIG_SUCCESS                = BASE + 15;
-    /* Indicates DHCP failed */
-    static final int CMD_IP_CONFIG_FAILURE                = BASE + 16;
+    /* Indicates Static IP succeded */
+    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
+    /* Indicates Static IP failed */
+    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
 
     /* Start the soft access point */
     static final int CMD_START_AP                         = BASE + 21;
@@ -338,8 +340,11 @@ public class WifiStateMachine extends HierarchicalStateMachine {
      */
     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
 
-    private static final int POWER_MODE_ACTIVE = 1;
-    private static final int POWER_MODE_AUTO = 0;
+    static final int POWER_MODE_ACTIVE = 1;
+    static final int POWER_MODE_AUTO = 0;
+
+    /* Tracks the power mode for restoration after a DHCP request/renewal goes through */
+    private int mPowerMode = POWER_MODE_AUTO;
 
     /**
      * See {@link Settings.Secure#WIFI_SCAN_INTERVAL_MS}. This is the default value if a
@@ -1392,8 +1397,10 @@ public class WifiStateMachine extends HierarchicalStateMachine {
          */
         NetworkUtils.resetConnections(mInterfaceName);
 
-        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
-            Log.e(TAG, "Could not stop DHCP");
+        if (mDhcpStateMachine != null) {
+            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+            mDhcpStateMachine.quit();
+            mDhcpStateMachine = null;
         }
 
         /* Disable interface */
@@ -1419,6 +1426,100 @@ public class WifiStateMachine extends HierarchicalStateMachine {
 
     }
 
+    void handlePreDhcpSetup() {
+        if (!mBluetoothConnectionActive) {
+            /*
+             * There are problems setting the Wi-Fi driver's power
+             * mode to active when bluetooth coexistence mode is
+             * enabled or sense.
+             * <p>
+             * We set Wi-Fi to active mode when
+             * obtaining an IP address because we've found
+             * compatibility issues with some routers with low power
+             * mode.
+             * <p>
+             * In order for this active power mode to properly be set,
+             * we disable coexistence mode until we're done with
+             * obtaining an IP address.  One exception is if we
+             * are currently connected to a headset, since disabling
+             * coexistence would interrupt that connection.
+             */
+            // Disable the coexistence mode
+            WifiNative.setBluetoothCoexistenceModeCommand(
+                    WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+        }
+
+        mPowerMode =  WifiNative.getPowerModeCommand();
+        if (mPowerMode < 0) {
+            // Handle the case where supplicant driver does not support
+            // getPowerModeCommand.
+            mPowerMode = WifiStateMachine.POWER_MODE_AUTO;
+        }
+        if (mPowerMode != WifiStateMachine.POWER_MODE_ACTIVE) {
+            WifiNative.setPowerModeCommand(WifiStateMachine.POWER_MODE_ACTIVE);
+        }
+    }
+
+
+    void handlePostDhcpSetup() {
+        /* restore power mode */
+        WifiNative.setPowerModeCommand(mPowerMode);
+
+        // Set the coexistence mode back to its default value
+        WifiNative.setBluetoothCoexistenceModeCommand(
+                WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+    }
+
+    private void handleSuccessfulIpConfiguration(DhcpInfoInternal dhcpInfoInternal) {
+        synchronized (mDhcpInfoInternal) {
+            mDhcpInfoInternal = dhcpInfoInternal;
+        }
+        mLastSignalLevel = -1; // force update of signal strength
+        WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
+        InetAddress addr = NetworkUtils.numericToInetAddress(dhcpInfoInternal.ipAddress);
+        mWifiInfo.setInetAddress(addr);
+        if (getNetworkDetailedState() == DetailedState.CONNECTED) {
+            //DHCP renewal in connected state
+            LinkProperties linkProperties = dhcpInfoInternal.makeLinkProperties();
+            linkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+            linkProperties.setInterfaceName(mInterfaceName);
+            if (!linkProperties.equals(mLinkProperties)) {
+                Log.d(TAG, "Link configuration changed for netId: " + mLastNetworkId
+                    + " old: " + mLinkProperties + "new: " + linkProperties);
+                NetworkUtils.resetConnections(mInterfaceName);
+                mLinkProperties = linkProperties;
+                sendLinkConfigurationChangedBroadcast();
+            }
+        } else {
+            configureLinkProperties();
+            setNetworkDetailedState(DetailedState.CONNECTED);
+            sendNetworkStateChangeBroadcast(mLastBssid);
+        }
+    }
+
+    private void handleFailedIpConfiguration() {
+        Log.e(TAG, "IP configuration failed");
+
+        mWifiInfo.setInetAddress(null);
+        /**
+         * If we've exceeded the maximum number of retries for DHCP
+         * to a given network, disable the network
+         */
+        if (++mReconnectCount > getMaxDhcpRetries()) {
+            Log.e(TAG, "Failed " +
+                    mReconnectCount + " times, Disabling " + mLastNetworkId);
+            WifiConfigStore.disableNetwork(mLastNetworkId);
+            mReconnectCount = 0;
+        }
+
+        /* DHCP times out after about 30 seconds, we do a
+         * disconnect and an immediate reconnect to try again
+         */
+        WifiNative.disconnectCommand();
+        WifiNative.reconnectCommand();
+
+    }
+
 
     /*********************************************************
      * Notifications from WifiMonitor
@@ -1590,6 +1691,8 @@ public class WifiStateMachine extends HierarchicalStateMachine {
                 case CMD_FORGET_NETWORK:
                 case CMD_RSSI_POLL:
                 case CMD_ENABLE_ALL_NETWORKS:
+                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                     break;
                 case CMD_START_WPS:
                     /* Return failure when the state machine cannot handle WPS initiation*/
@@ -2462,74 +2565,18 @@ public class WifiStateMachine extends HierarchicalStateMachine {
     }
 
     class ConnectingState extends HierarchicalState {
-        boolean mModifiedBluetoothCoexistenceMode;
-        int mPowerMode;
-        boolean mUseStaticIp;
-        Thread mDhcpThread;
 
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-            mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
-            if (!mUseStaticIp) {
-                mDhcpThread = null;
-                mModifiedBluetoothCoexistenceMode = false;
-                mPowerMode = POWER_MODE_AUTO;
-
-                if (!mBluetoothConnectionActive) {
-                    /*
-                     * There are problems setting the Wi-Fi driver's power
-                     * mode to active when bluetooth coexistence mode is
-                     * enabled or sense.
-                     * <p>
-                     * We set Wi-Fi to active mode when
-                     * obtaining an IP address because we've found
-                     * compatibility issues with some routers with low power
-                     * mode.
-                     * <p>
-                     * In order for this active power mode to properly be set,
-                     * we disable coexistence mode until we're done with
-                     * obtaining an IP address.  One exception is if we
-                     * are currently connected to a headset, since disabling
-                     * coexistence would interrupt that connection.
-                     */
-                    mModifiedBluetoothCoexistenceMode = true;
-
-                    // Disable the coexistence mode
-                    WifiNative.setBluetoothCoexistenceModeCommand(
-                            WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
-                }
-
-                mPowerMode =  WifiNative.getPowerModeCommand();
-                if (mPowerMode < 0) {
-                  // Handle the case where supplicant driver does not support
-                  // getPowerModeCommand.
-                    mPowerMode = POWER_MODE_AUTO;
-                }
-                if (mPowerMode != POWER_MODE_ACTIVE) {
-                    WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE);
-                }
 
-                Log.d(TAG, "DHCP request started");
-                mDhcpThread = new Thread(new Runnable() {
-                    public void run() {
-                        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
-                        if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal)) {
-                            Log.d(TAG, "DHCP request succeeded");
-                            synchronized (mDhcpInfoInternal) {
-                                mDhcpInfoInternal = dhcpInfoInternal;
-                            }
-                            WifiConfigStore.setIpConfiguration(mLastNetworkId, dhcpInfoInternal);
-                            sendMessage(CMD_IP_CONFIG_SUCCESS);
-                        } else {
-                            Log.d(TAG, "DHCP request failed: " +
-                                    NetworkUtils.getDhcpError());
-                            sendMessage(CMD_IP_CONFIG_FAILURE);
-                        }
-                    }
-                });
-                mDhcpThread.start();
+             if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+                //start DHCP
+                mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+                        mContext, WifiStateMachine.this, mInterfaceName);
+                mDhcpStateMachine.registerForPreDhcpNotification();
+                mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
             } else {
                 DhcpInfoInternal dhcpInfoInternal = WifiConfigStore.getIpConfiguration(
                         mLastNetworkId);
@@ -2541,16 +2588,13 @@ public class WifiStateMachine extends HierarchicalStateMachine {
                 try {
                     netd.setInterfaceConfig(mInterfaceName, ifcg);
                     Log.v(TAG, "Static IP configuration succeeded");
-                    synchronized (mDhcpInfoInternal) {
-                        mDhcpInfoInternal = dhcpInfoInternal;
-                    }
-                    sendMessage(CMD_IP_CONFIG_SUCCESS);
+                    sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
                 } catch (RemoteException re) {
                     Log.v(TAG, "Static IP configuration failed: " + re);
-                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                    sendMessage(CMD_STATIC_IP_FAILURE);
                 } catch (IllegalStateException e) {
                     Log.v(TAG, "Static IP configuration failed: " + e);
-                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                    sendMessage(CMD_STATIC_IP_FAILURE);
                 }
             }
          }
@@ -2559,44 +2603,26 @@ public class WifiStateMachine extends HierarchicalStateMachine {
           if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
 
           switch(message.what) {
-              case CMD_IP_CONFIG_SUCCESS:
-                  mLastSignalLevel = -1; // force update of signal strength
-                  InetAddress addr;
-                  synchronized (mDhcpInfoInternal) {
-                      addr = NetworkUtils.numericToInetAddress(mDhcpInfoInternal.ipAddress);
-                  }
-                  mWifiInfo.setInetAddress(addr);
-                  configureLinkProperties();
-                  if (getNetworkDetailedState() == DetailedState.CONNECTED) {
-                      sendLinkConfigurationChangedBroadcast();
-                  } else {
-                      setNetworkDetailedState(DetailedState.CONNECTED);
-                      sendNetworkStateChangeBroadcast(mLastBssid);
+              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                  handlePreDhcpSetup();
+                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                  break;
+              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                  handlePostDhcpSetup();
+                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+                      transitionTo(mConnectedState);
+                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+                      handleFailedIpConfiguration();
+                      transitionTo(mDisconnectingState);
                   }
-                  //TODO: The framework is not detecting a DHCP renewal and a possible
-                  //IP change. we should detect this and send out a config change broadcast
+                  break;
+              case CMD_STATIC_IP_SUCCESS:
+                  handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
                   transitionTo(mConnectedState);
                   break;
-              case CMD_IP_CONFIG_FAILURE:
-                  mWifiInfo.setInetAddress(null);
-
-                  Log.e(TAG, "IP configuration failed");
-                  /**
-                   * If we've exceeded the maximum number of retries for DHCP
-                   * to a given network, disable the network
-                   */
-                  if (++mReconnectCount > getMaxDhcpRetries()) {
-                      Log.e(TAG, "Failed " +
-                              mReconnectCount + " times, Disabling " + mLastNetworkId);
-                      WifiConfigStore.disableNetwork(mLastNetworkId);
-                      mReconnectCount = 0;
-                  }
-
-                  /* DHCP times out after about 30 seconds, we do a
-                   * disconnect and an immediate reconnect to try again
-                   */
-                  WifiNative.disconnectCommand();
-                  WifiNative.reconnectCommand();
+              case CMD_STATIC_IP_FAILURE:
+                  handleFailedIpConfiguration();
                   transitionTo(mDisconnectingState);
                   break;
               case CMD_DISCONNECT:
@@ -2640,23 +2666,6 @@ public class WifiStateMachine extends HierarchicalStateMachine {
           EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
           return HANDLED;
       }
-
-      @Override
-      public void exit() {
-          /* reset power state & bluetooth coexistence if on DHCP */
-          if (!mUseStaticIp) {
-              if (mPowerMode != POWER_MODE_ACTIVE) {
-                  WifiNative.setPowerModeCommand(mPowerMode);
-              }
-
-              if (mModifiedBluetoothCoexistenceMode) {
-                  // Set the coexistence mode back to its default value
-                  WifiNative.setBluetoothCoexistenceModeCommand(
-                          WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
-              }
-          }
-
-      }
     }
 
     class ConnectedState extends HierarchicalState {
@@ -2674,6 +2683,19 @@ public class WifiStateMachine extends HierarchicalStateMachine {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             boolean eventLoggingEnabled = true;
             switch (message.what) {
+              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                  handlePreDhcpSetup();
+                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                  break;
+              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                  handlePostDhcpSetup();
+                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+                      handleFailedIpConfiguration();
+                      transitionTo(mDisconnectingState);
+                  }
+                  break;
                 case CMD_DISCONNECT:
                     WifiNative.disconnectCommand();
                     transitionTo(mDisconnectingState);