Add per network static IP settings

Remove the existing global static IP settings and add support
for per network configuration

Change-Id: I5a6d8b877471b8c8ad07951c96d273893754607f
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 1178bec..9c81c19 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -37,6 +37,19 @@
         super();
     }
 
+    /** copy constructor {@hide} */
+    public DhcpInfo(DhcpInfo source) {
+        if (source != null) {
+            ipAddress = source.ipAddress;
+            gateway = source.gateway;
+            netmask = source.netmask;
+            dns1 = source.dns1;
+            dns2 = source.dns2;
+            serverAddress = source.serverAddress;
+            leaseDuration = source.leaseDuration;
+        }
+    }
+
     public String toString() {
         StringBuffer str = new StringBuffer();
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e1c84ef..5b4aedb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1128,6 +1128,7 @@
          */
         public static final int WIFI_SLEEP_POLICY_NEVER = 2;
 
+        //TODO: deprecate static IP constants
         /**
          * Whether to use static IP and other static network attributes.
          * <p>
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 78d5b7e..dfa9f75 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -19,12 +19,22 @@
 import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
+import android.net.DhcpInfo;
+import android.net.wifi.WifiConfiguration.IpAssignment;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.Status;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -34,9 +44,23 @@
  *
  * It deals with the following
  * - Add/update/remove a WifiConfiguration
+ *   The configuration contains two types of information.
+ *     = IP configuration that is handled by WifiConfigStore and
+ *       is saved to disk on any change.
+ *     = SSID & security details that is pushed to the supplicant.
+ *       supplicant saves these details to the disk on calling
+ *       saveConfigCommand().
+ *
+ *       We have two kinds of APIs exposed:
+ *        > public API calls that provide fine grained control
+ *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
+ *          removeNetwork(). For these calls, the config is not persisted
+ *          to the disk. (TODO: deprecate these calls in WifiManager)
+ *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
+ *          These calls persist the supplicant config to disk.
  * - Maintain a list of configured networks for quick access
+ *
  * TODO:
- * - handle static IP per configuration
  * - handle proxy per configuration
  */
 class WifiConfigStore {
@@ -44,10 +68,28 @@
     private static Context sContext;
     private static final String TAG = "WifiConfigStore";
 
-    private static List<WifiConfiguration> sConfiguredNetworks = new ArrayList<WifiConfiguration>();
+    /* configured networks with network id as the key */
+    private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
+            new HashMap<Integer, WifiConfiguration>();
+
+    /* A network id is a unique identifier for a network configured in the
+     * supplicant. Network ids are generated when the supplicant reads
+     * the configuration file at start and can thus change for networks.
+     * We store the IP configuration for networks along with a unique id
+     * that is generated from SSID and security type of the network. A mapping
+     * from the generated unique id to network id of the network is needed to
+     * map supplicant config to IP configuration. */
+    private static HashMap<Integer, Integer> sNetworkIds =
+            new HashMap<Integer, Integer>();
+
     /* Tracks the highest priority of configured networks */
     private static int sLastPriority = -1;
 
+    private static final String ipConfigFile = Environment.getDataDirectory() +
+            "/misc/wifi/ipconfig.txt";
+
+    private static final int IPCONFIG_FILE_VERSION = 1;
+
     /**
      * Initialize context, fetch the list of configured networks
      * and enable all stored networks in supplicant.
@@ -66,7 +108,7 @@
     static List<WifiConfiguration> getConfiguredNetworks() {
         List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
         synchronized (sConfiguredNetworks) {
-            for (WifiConfiguration config : sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
                 networks.add(config.clone());
             }
         }
@@ -78,14 +120,20 @@
      * of configured networks indicates all networks as being enabled
      */
     static void enableAllNetworks() {
-        for (WifiConfiguration config : sConfiguredNetworks) {
-            if(config != null && config.status == Status.DISABLED) {
-                WifiNative.enableNetworkCommand(config.networkId, false);
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if(config != null && config.status == Status.DISABLED) {
+                    if(WifiNative.enableNetworkCommand(config.networkId, false)) {
+                        config.status = Status.ENABLED;
+                    } else {
+                        Log.e(TAG, "Enable network failed on " + config.networkId);
+                    }
+                }
             }
         }
 
         WifiNative.saveConfigCommand();
-        updateConfigAndSendChangeBroadcast();
+        sendConfigChangeBroadcast();
     }
 
     /**
@@ -102,7 +150,11 @@
     static void selectNetwork(WifiConfiguration config) {
         if (config != null) {
             int netId = addOrUpdateNetworkNative(config);
-            selectNetwork(netId);
+            if (netId != -1) {
+                selectNetwork(netId);
+            } else {
+                Log.e(TAG, "Failed to update network " + config);
+            }
         }
     }
 
@@ -120,10 +172,12 @@
     static void selectNetwork(int netId) {
         // Reset the priority of each network at start or if it goes too high.
         if (sLastPriority == -1 || sLastPriority > 1000000) {
-            for (WifiConfiguration conf : sConfiguredNetworks) {
-                if (conf.networkId != -1) {
-                    conf.priority = 0;
-                    addOrUpdateNetworkNative(conf);
+            synchronized (sConfiguredNetworks) {
+                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                    if (config.networkId != -1) {
+                        config.priority = 0;
+                        addOrUpdateNetworkNative(config);
+                    }
                 }
             }
             sLastPriority = 0;
@@ -138,13 +192,10 @@
         WifiNative.saveConfigCommand();
 
         /* Enable the given network while disabling all other networks */
-        WifiNative.enableNetworkCommand(netId, true);
+        enableNetworkWithoutBroadcast(netId, true);
 
-        /* update the configured networks list but not send a
-         * broadcast to avoid a fetch from settings
-         * during this temporary disabling of networks
-         */
-        updateConfiguredNetworks();
+       /* Avoid saving the config & sending a broadcast to prevent settings
+        * from displaying a disabled list of networks */
     }
 
     /**
@@ -153,13 +204,17 @@
      * @param config WifiConfiguration to be saved
      */
     static void saveNetwork(WifiConfiguration config) {
+        boolean newNetwork = (config.networkId == -1);
         int netId = addOrUpdateNetworkNative(config);
         /* enable a new network */
-        if (config.networkId < 0) {
+        if (newNetwork && netId >= 0) {
             WifiNative.enableNetworkCommand(netId, false);
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.get(netId).status = Status.ENABLED;
+            }
         }
         WifiNative.saveConfigCommand();
-        updateConfigAndSendChangeBroadcast();
+        sendConfigChangeBroadcast();
     }
 
     /**
@@ -168,9 +223,15 @@
      * @param netId network to forget
      */
     static void forgetNetwork(int netId) {
-        WifiNative.removeNetworkCommand(netId);
-        WifiNative.saveConfigCommand();
-        updateConfigAndSendChangeBroadcast();
+        if (WifiNative.removeNetworkCommand(netId)) {
+            WifiNative.saveConfigCommand();
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.remove(netId);
+            }
+            sendConfigChangeBroadcast();
+        } else {
+            Log.e(TAG, "Failed to remove network " + netId);
+        }
     }
 
     /**
@@ -183,7 +244,7 @@
      */
     static int addOrUpdateNetwork(WifiConfiguration config) {
         int ret = addOrUpdateNetworkNative(config);
-        updateConfigAndSendChangeBroadcast();
+        sendConfigChangeBroadcast();
         return ret;
     }
 
@@ -197,7 +258,10 @@
      */
     static boolean removeNetwork(int netId) {
         boolean ret = WifiNative.removeNetworkCommand(netId);
-        updateConfigAndSendChangeBroadcast();
+        synchronized (sConfiguredNetworks) {
+            if (ret) sConfiguredNetworks.remove(netId);
+        }
+        sendConfigChangeBroadcast();
         return ret;
     }
 
@@ -210,8 +274,28 @@
      * @param netId network to be removed
      */
     static boolean enableNetwork(int netId, boolean disableOthers) {
+        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
+        sendConfigChangeBroadcast();
+        return ret;
+    }
+
+    static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
         boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
-        updateConfigAndSendChangeBroadcast();
+
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) config.status = Status.ENABLED;
+        }
+
+        if (disableOthers) {
+            synchronized (sConfiguredNetworks) {
+                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                    if(config != null && config.networkId != netId) {
+                        config.status = Status.DISABLED;
+                    }
+                }
+            }
+        }
         return ret;
     }
 
@@ -221,7 +305,11 @@
      */
     static boolean disableNetwork(int netId) {
         boolean ret = WifiNative.disableNetworkCommand(netId);
-        updateConfigAndSendChangeBroadcast();
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) config.status = Status.DISABLED;
+        }
+        sendConfigChangeBroadcast();
         return ret;
     }
 
@@ -232,8 +320,31 @@
         return WifiNative.saveConfigCommand();
     }
 
-    private static void updateConfigAndSendChangeBroadcast() {
-        updateConfiguredNetworks();
+    /**
+     * Fetch the IP configuration for a given network id
+     */
+    static DhcpInfo getIpConfiguration(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) return new DhcpInfo(config.ipConfig);
+        }
+        return null;
+    }
+
+    /**
+     * Return if the specified network is using static IP
+     */
+    static boolean isUsingStaticIp(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static void sendConfigChangeBroadcast() {
         if (!ActivityManagerNative.isSystemReady()) return;
         Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
         sContext.sendBroadcast(intent);
@@ -245,6 +356,7 @@
 
         synchronized (sConfiguredNetworks) {
             sConfiguredNetworks.clear();
+            sNetworkIds.clear();
 
             if (listStr == null)
                 return;
@@ -274,9 +386,129 @@
                 if (config.priority > sLastPriority) {
                     sLastPriority = config.priority;
                 }
-                sConfiguredNetworks.add(config);
+                sConfiguredNetworks.put(config.networkId, config);
+                sNetworkIds.put(configKey(config), config.networkId);
             }
         }
+        readIpConfigurations();
+    }
+
+    private static void writeIpConfigurations() {
+        StringBuilder builder = new StringBuilder();
+        BufferedWriter out = null;
+
+        builder.append(IPCONFIG_FILE_VERSION);
+        builder.append("\n");
+
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if (config.ipAssignment == WifiConfiguration.IpAssignment.STATIC) {
+                    builder.append("id=" + configKey(config));
+                    builder.append(":");
+                    builder.append("ip=" + config.ipConfig.ipAddress);
+                    builder.append(":");
+                    builder.append("gateway=" + config.ipConfig.gateway);
+                    builder.append(":");
+                    builder.append("netmask=" + config.ipConfig.netmask);
+                    builder.append(":");
+                    builder.append("dns1=" + config.ipConfig.dns1);
+                    builder.append(":");
+                    builder.append("dns2=" + config.ipConfig.dns2);
+                    builder.append("\n");
+                }
+            }
+        }
+
+        try {
+            out = new BufferedWriter(new FileWriter(ipConfigFile), builder.length());
+            out.write(builder.toString());
+        } catch (IOException e) {
+            Log.e(TAG, "Error writing data file");
+            return;
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (Exception e) {}
+            }
+        }
+    }
+
+    private static void readIpConfigurations() {
+        File f = new File(ipConfigFile);
+        byte[] buffer;
+        FileInputStream s = null;
+        try {
+            buffer = new byte[(int)f.length()];
+            s = new FileInputStream(f);
+            s.read(buffer);
+        } catch (IOException e) {
+            Log.e(TAG, "Error reading data file");
+            return;
+        } finally {
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (Exception e) {}
+            }
+        }
+
+        String data = new String(buffer);
+        if (data == null || data.length() == 0) {
+            Log.d(TAG, "IP configuration file empty");
+            return;
+        }
+
+        String[] parsed = data.split("\n");
+        try {
+            if (Integer.parseInt(parsed[0]) != IPCONFIG_FILE_VERSION) {
+                Log.e(TAG, "Bad version on IP configuration file, ignore read");
+                return;
+            }
+
+            for (String line : parsed) {
+                int hashKey = -1;
+                DhcpInfo ipConfig = new DhcpInfo();
+                String[] keyVals = line.split(":");
+
+                for (String keyVal : keyVals) {
+                    String[] keyValPair = keyVal.split("=");
+                    if (keyValPair[0].equals("id")) {
+                        hashKey = Integer.parseInt(keyValPair[1]);
+                    } else if (keyValPair[0].equals("ip")) {
+                        ipConfig.ipAddress = Integer.parseInt(keyValPair[1]);
+                    } else if (keyValPair[0].equals("gateway")) {
+                        ipConfig.gateway = Integer.parseInt(keyValPair[1]);
+                    } else if (keyValPair[0].equals("netmask")) {
+                        ipConfig.netmask = Integer.parseInt(keyValPair[1]);
+                    } else if (keyValPair[0].equals("dns1")) {
+                        ipConfig.dns1 = Integer.parseInt(keyValPair[1]);
+                    } else if (keyValPair[0].equals("dns2")) {
+                        ipConfig.dns2 = Integer.parseInt(keyValPair[1]);
+                    } else {
+                        Log.w(TAG, "Ignoring " + keyVal);
+                    }
+                }
+
+                if (hashKey != -1) {
+                    synchronized (sConfiguredNetworks) {
+                        WifiConfiguration config = sConfiguredNetworks.get(
+                                sNetworkIds.get(hashKey));
+
+                        if (config == null) {
+                            Log.e(TAG, "IP configuration found for missing network, ignored");
+                        } else {
+                            config.ipAssignment = WifiConfiguration.IpAssignment.STATIC;
+                            config.ipConfig = ipConfig;
+                        }
+                    }
+                } else {
+                    Log.e(TAG,"Missing id while parsing configuration" + line);
+                }
+            }
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Error parsing configuration");
+        }
     }
 
     private static int addOrUpdateNetworkNative(WifiConfiguration config) {
@@ -286,6 +518,7 @@
          * refer to an existing configuration.
          */
         int netId = config.networkId;
+        boolean updateFailed = true;
         boolean newNetwork = netId == -1;
         // networkId of -1 means we want to create a new network
 
@@ -457,17 +690,53 @@
                     }
                 }
             }
-            return netId;
+            updateFailed = false;
         }
 
-        if (newNetwork) {
-            WifiNative.removeNetworkCommand(netId);
-            Log.d(TAG,
-                    "Failed to set a network variable, removed network: "
-                    + netId);
+        if (updateFailed) {
+            if (newNetwork) {
+                WifiNative.removeNetworkCommand(netId);
+                Log.d(TAG,
+                        "Failed to set a network variable, removed network: "
+                        + netId);
+            }
+            return -1;
         }
 
-        return -1;
+        /* An update of the network variables requires reading them
+         * back from the supplicant to update sConfiguredNetworks.
+         * This is because some of the variables (SSID, wep keys &
+         * passphrases) reflect different values when read back than
+         * when written. For example, wep key is stored as * irrespective
+         * of the value sent to the supplicant
+         */
+        WifiConfiguration sConfig;
+        synchronized (sConfiguredNetworks) {
+            sConfig = sConfiguredNetworks.get(netId);
+        }
+        if (sConfig == null) {
+            sConfig = new WifiConfiguration();
+            sConfig.networkId = netId;
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.put(netId, sConfig);
+            }
+        }
+        readNetworkVariables(sConfig);
+
+        if (config.ipAssignment != IpAssignment.UNASSIGNED) {
+            if (newNetwork ||
+                    (sConfig.ipAssignment != config.ipAssignment) ||
+                    (sConfig.ipConfig.ipAddress != config.ipConfig.ipAddress) ||
+                    (sConfig.ipConfig.gateway != config.ipConfig.gateway) ||
+                    (sConfig.ipConfig.netmask != config.ipConfig.netmask) ||
+                    (sConfig.ipConfig.dns1 != config.ipConfig.dns1) ||
+                    (sConfig.ipConfig.dns2 != config.ipConfig.dns2)) {
+                sConfig.ipAssignment = config.ipAssignment;
+                sConfig.ipConfig = config.ipConfig;
+                writeIpConfigurations();
+            }
+        }
+        return netId;
     }
 
     /**
@@ -669,6 +938,24 @@
         return -1;
     }
 
+    /* Returns a unique for a given configuration */
+    private static int configKey(WifiConfiguration config) {
+        String key;
+
+        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+        } else if (config.wepKeys[0] != null) {
+            key = config.SSID + "WEP";
+        } else {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
+        }
+
+        return key.hashCode();
+    }
+
     static String dump() {
         StringBuffer sb = new StringBuffer();
         String LS = System.getProperty("line.separator");
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ca4f29f..8971bdd 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.net.DhcpInfo;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -294,6 +295,22 @@
      */
     public BitSet allowedGroupCiphers;
 
+    /**
+     * @hide
+     */
+    public enum IpAssignment {
+        STATIC,
+        DHCP,
+        UNASSIGNED
+    }
+    /**
+     * @hide
+     */
+    public IpAssignment ipAssignment;
+    /**
+     * @hide
+     */
+    public DhcpInfo ipConfig;
 
     public WifiConfiguration() {
         networkId = -1;
@@ -312,6 +329,8 @@
         for (EnterpriseField field : enterpriseFields) {
             field.setValue(null);
         }
+        ipAssignment = IpAssignment.UNASSIGNED;
+        ipConfig = new DhcpInfo();
     }
 
     public String toString() {
@@ -393,6 +412,11 @@
             if (value != null) sbuf.append(value);
         }
         sbuf.append('\n');
+        if (ipAssignment == IpAssignment.STATIC) {
+            sbuf.append(" ").append("Static IP configuration:").append('\n');
+            sbuf.append(" ").append(ipConfig);
+        }
+        sbuf.append('\n');
         return sbuf.toString();
     }
 
@@ -461,6 +485,8 @@
         for (int i = 0; i < enterpriseFields.length; i++) {
             config.enterpriseFields[i].setValue(enterpriseFields[i].value());
         }
+        config.ipAssignment = ipAssignment;
+        config.ipConfig = new DhcpInfo(ipConfig);
         return config;
     }
 
@@ -486,6 +512,14 @@
         for (EnterpriseField field : enterpriseFields) {
             dest.writeString(field.value());
         }
+        dest.writeString(ipAssignment.name());
+        dest.writeInt(ipConfig.ipAddress);
+        dest.writeInt(ipConfig.netmask);
+        dest.writeInt(ipConfig.gateway);
+        dest.writeInt(ipConfig.dns1);
+        dest.writeInt(ipConfig.dns2);
+        dest.writeInt(ipConfig.serverAddress);
+        dest.writeInt(ipConfig.leaseDuration);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -512,6 +546,15 @@
                 for (EnterpriseField field : config.enterpriseFields) {
                     field.setValue(in.readString());
                 }
+
+                config.ipAssignment = IpAssignment.valueOf(in.readString());
+                config.ipConfig.ipAddress = in.readInt();
+                config.ipConfig.netmask = in.readInt();
+                config.ipConfig.gateway = in.readInt();
+                config.ipConfig.dns1 = in.readInt();
+                config.ipConfig.dns2 = in.readInt();
+                config.ipConfig.serverAddress = in.readInt();
+                config.ipConfig.leaseDuration = in.readInt();
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e1cbf5b..075cca4 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -123,10 +123,8 @@
     private int mLastNetworkId;
     private boolean mEnableRssiPolling = false;
     private boolean mPasswordKeyMayBeIncorrect = false;
-    private boolean mUseStaticIp = false;
     private int mReconnectCount = 0;
     private boolean mIsScanMode = false;
-    private boolean mConfigChanged = false;
 
     /**
      * Instance of the bluetooth headset helper. This needs to be created
@@ -138,10 +136,6 @@
 
     private BluetoothA2dp mBluetoothA2dp;
 
-    /**
-     * Observes the static IP address settings.
-     */
-    private SettingsObserver mSettingsObserver;
     private LinkProperties mLinkProperties;
 
     // Held during driver load and unload
@@ -447,8 +441,6 @@
                 }
         };
 
-        mSettingsObserver = new SettingsObserver(new Handler());
-
         PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
         sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
@@ -596,7 +588,9 @@
     }
 
     public DhcpInfo syncGetDhcpInfo() {
-        return mDhcpInfo;
+        synchronized (mDhcpInfo) {
+            return new DhcpInfo(mDhcpInfo);
+        }
     }
 
     /**
@@ -909,10 +903,8 @@
         sb.append("mEnableAllNetworks ").append(mEnableAllNetworks).append(LS);
         sb.append("mEnableRssiPolling ").append(mEnableRssiPolling).append(LS);
         sb.append("mPasswordKeyMayBeIncorrect ").append(mPasswordKeyMayBeIncorrect).append(LS);
-        sb.append("mUseStaticIp ").append(mUseStaticIp).append(LS);
         sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
         sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
-        sb.append("mConfigChanged ").append(mConfigChanged).append(LS).append(LS);
         sb.append("Supplicant status").append(LS)
                 .append(WifiNative.statusCommand()).append(LS).append(LS);
 
@@ -1196,140 +1188,21 @@
             return;
         }
         // TODO - fix this for v6
-        mLinkProperties.addAddress(NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress));
-        mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
-        mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
-        mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
+        synchronized (mDhcpInfo) {
+            mLinkProperties.addAddress(NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress));
+            mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
+            mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
+            mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
+        }
         // TODO - add proxy info
     }
 
-
-    private void checkUseStaticIp() {
-        mUseStaticIp = false;
-        final ContentResolver cr = mContext.getContentResolver();
-        try {
-            if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
-                return;
-            }
-        } catch (Settings.SettingNotFoundException e) {
-            return;
-        }
-
-        try {
-            String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
-            if (addr != null) {
-                mDhcpInfo.ipAddress = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
-            if (addr != null) {
-                mDhcpInfo.gateway = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
-            if (addr != null) {
-                mDhcpInfo.netmask = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
-            if (addr != null) {
-                mDhcpInfo.dns1 = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
-            if (addr != null) {
-                mDhcpInfo.dns2 = stringToIpAddr(addr);
-            } else {
-                mDhcpInfo.dns2 = 0;
-            }
-        } catch (UnknownHostException e) {
-            return;
-        }
-        mUseStaticIp = true;
-    }
-
-    private static int stringToIpAddr(String addrString) throws UnknownHostException {
-        try {
-            String[] parts = addrString.split("\\.");
-            if (parts.length != 4) {
-                throw new UnknownHostException(addrString);
-            }
-
-            int a = Integer.parseInt(parts[0])      ;
-            int b = Integer.parseInt(parts[1]) <<  8;
-            int c = Integer.parseInt(parts[2]) << 16;
-            int d = Integer.parseInt(parts[3]) << 24;
-
-            return a | b | c | d;
-        } catch (NumberFormatException ex) {
-            throw new UnknownHostException(addrString);
-        }
-    }
-
     private int getMaxDhcpRetries() {
         return Settings.Secure.getInt(mContext.getContentResolver(),
                                       Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
                                       DEFAULT_MAX_DHCP_RETRIES);
     }
 
-    private class SettingsObserver extends ContentObserver {
-        public SettingsObserver(Handler handler) {
-            super(handler);
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_USE_STATIC_IP), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_IP), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_GATEWAY), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_NETMASK), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_DNS1), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_DNS2), false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-
-            boolean wasStaticIp = mUseStaticIp;
-            int oIp, oGw, oMsk, oDns1, oDns2;
-            oIp = oGw = oMsk = oDns1 = oDns2 = 0;
-            if (wasStaticIp) {
-                oIp = mDhcpInfo.ipAddress;
-                oGw = mDhcpInfo.gateway;
-                oMsk = mDhcpInfo.netmask;
-                oDns1 = mDhcpInfo.dns1;
-                oDns2 = mDhcpInfo.dns2;
-            }
-            checkUseStaticIp();
-
-            if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
-                return;
-            }
-
-            boolean changed =
-                (wasStaticIp != mUseStaticIp) ||
-                    (wasStaticIp && (
-                        oIp   != mDhcpInfo.ipAddress ||
-                        oGw   != mDhcpInfo.gateway ||
-                        oMsk  != mDhcpInfo.netmask ||
-                        oDns1 != mDhcpInfo.dns1 ||
-                        oDns2 != mDhcpInfo.dns2));
-
-            if (changed) {
-                sendMessage(CMD_RECONFIGURE_IP);
-                mConfigChanged = true;
-            }
-        }
-    }
-
     /**
      * Whether to disable coexistence mode while obtaining IP address. This
      * logic will return true only if the current bluetooth
@@ -1382,6 +1255,7 @@
         mContext.sendStickyBroadcast(intent);
     }
 
+    /* TODO: Unused for now, will be used when ip change on connected network is handled */
     private void sendConfigChangeBroadcast() {
         if (!ActivityManagerNative.isSystemReady()) return;
         Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
@@ -1969,7 +1843,6 @@
                     }
                     checkIsBluetoothPlaying();
 
-                    checkUseStaticIp();
                     sendSupplicantConnectionChangedBroadcast(true);
                     transitionTo(mDriverSupReadyState);
                     break;
@@ -2477,8 +2350,9 @@
     }
 
     class ConnectingState extends HierarchicalState {
-        boolean modifiedBluetoothCoexistenceMode;
-        int powerMode;
+        boolean mModifiedBluetoothCoexistenceMode;
+        int mPowerMode;
+        boolean mUseStaticIp;
         Thread mDhcpThread;
 
         @Override
@@ -2486,11 +2360,11 @@
             if (DBG) Log.d(TAG, getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
 
+            mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
             if (!mUseStaticIp) {
-
                 mDhcpThread = null;
-                modifiedBluetoothCoexistenceMode = false;
-                powerMode = DRIVER_POWER_MODE_AUTO;
+                mModifiedBluetoothCoexistenceMode = false;
+                mPowerMode = DRIVER_POWER_MODE_AUTO;
 
                 if (shouldDisableCoexistenceMode()) {
                     /*
@@ -2509,28 +2383,32 @@
                      * are currently connected to a headset, since disabling
                      * coexistence would interrupt that connection.
                      */
-                    modifiedBluetoothCoexistenceMode = true;
+                    mModifiedBluetoothCoexistenceMode = true;
 
                     // Disable the coexistence mode
                     WifiNative.setBluetoothCoexistenceModeCommand(
                             WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
                 }
 
-                powerMode =  WifiNative.getPowerModeCommand();
-                if (powerMode < 0) {
+                mPowerMode =  WifiNative.getPowerModeCommand();
+                if (mPowerMode < 0) {
                   // Handle the case where supplicant driver does not support
                   // getPowerModeCommand.
-                    powerMode = DRIVER_POWER_MODE_AUTO;
+                    mPowerMode = DRIVER_POWER_MODE_AUTO;
                 }
-                if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+                if (mPowerMode != DRIVER_POWER_MODE_ACTIVE) {
                     WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
                 }
 
                 Log.d(TAG, "DHCP request started");
                 mDhcpThread = new Thread(new Runnable() {
                     public void run() {
-                        if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
+                        DhcpInfo dhcpInfo = new DhcpInfo();
+                        if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfo)) {
                             Log.d(TAG, "DHCP request succeeded");
+                            synchronized (mDhcpInfo) {
+                                mDhcpInfo = dhcpInfo;
+                            }
                             sendMessage(CMD_IP_CONFIG_SUCCESS);
                         } else {
                             Log.d(TAG, "DHCP request failed: " +
@@ -2541,8 +2419,12 @@
                 });
                 mDhcpThread.start();
             } else {
-                if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
+                DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId);
+                if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) {
                     Log.v(TAG, "Static IP configuration succeeded");
+                    synchronized (mDhcpInfo) {
+                        mDhcpInfo = dhcpInfo;
+                    }
                     sendMessage(CMD_IP_CONFIG_SUCCESS);
                 } else {
                     Log.v(TAG, "Static IP configuration failed");
@@ -2558,18 +2440,15 @@
               case CMD_IP_CONFIG_SUCCESS:
                   mReconnectCount = 0;
                   mLastSignalLevel = -1; // force update of signal strength
-                  mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
-                  Log.d(TAG, "IP configuration: " + mDhcpInfo);
+                  synchronized (mDhcpInfo) {
+                      mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+                      Log.d(TAG, "IP configuration: " + mDhcpInfo);
+                  }
                   configureLinkProperties();
                   setDetailedState(DetailedState.CONNECTED);
                   sendNetworkStateChangeBroadcast(mLastBssid);
-                  //TODO: we could also detect an IP config change
-                  // from a DHCP renewal and send out a config change
-                  // broadcast
-                  if (mConfigChanged) {
-                      sendConfigChangeBroadcast();
-                      mConfigChanged = false;
-                  }
+                  //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
                   transitionTo(mConnectedState);
                   break;
               case CMD_IP_CONFIG_FAILURE:
@@ -2631,11 +2510,11 @@
       public void exit() {
           /* reset power state & bluetooth coexistence if on DHCP */
           if (!mUseStaticIp) {
-              if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
-                  WifiNative.setPowerModeCommand(powerMode);
+              if (mPowerMode != DRIVER_POWER_MODE_ACTIVE) {
+                  WifiNative.setPowerModeCommand(mPowerMode);
               }
 
-              if (modifiedBluetoothCoexistenceMode) {
+              if (mModifiedBluetoothCoexistenceMode) {
                   // Set the coexistence mode back to its default value
                   WifiNative.setBluetoothCoexistenceModeCommand(
                           WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);