Use LinkProperties for IP and proxy configuration

Change-Id: I4ae817fb00141e9a742216b7fd02dca1ed228270
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index cb302da..3f03a2a 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -34,26 +34,26 @@
     private final InetAddress address;
 
     /**
-     * Network prefix
+     * Network prefix length
      */
-    private final int prefix;
+    private final int prefixLength;
 
     public LinkAddress(InetAddress address, InetAddress mask) {
         this.address = address;
-        this.prefix = computeprefix(mask);
+        this.prefixLength = computeprefixLength(mask);
     }
 
-    public LinkAddress(InetAddress address, int prefix) {
+    public LinkAddress(InetAddress address, int prefixLength) {
         this.address = address;
-        this.prefix = prefix;
+        this.prefixLength = prefixLength;
     }
 
     public LinkAddress(InterfaceAddress interfaceAddress) {
         this.address = interfaceAddress.getAddress();
-        this.prefix = interfaceAddress.getNetworkPrefixLength();
+        this.prefixLength = interfaceAddress.getNetworkPrefixLength();
     }
 
-    private static int computeprefix(InetAddress mask) {
+    private static int computeprefixLength(InetAddress mask) {
         int count = 0;
         for (byte b : mask.getAddress()) {
             for (int i = 0; i < 8; ++i) {
@@ -67,12 +67,12 @@
 
     @Override
     public String toString() {
-        return (address == null ? "" : (address.getHostAddress() + "/" + prefix));
+        return (address == null ? "" : (address.getHostAddress() + "/" + prefixLength));
     }
 
     /**
      * Compares this {@code LinkAddress} instance against the specified address
-     * in {@code obj}. Two addresses are equal if their InetAddress and prefix
+     * in {@code obj}. Two addresses are equal if their InetAddress and prefixLength
      * are equal
      *
      * @param obj the object to be tested for equality.
@@ -85,7 +85,7 @@
         }
         LinkAddress linkAddress = (LinkAddress) obj;
         return this.address.equals(linkAddress.address) &&
-            this.prefix == linkAddress.prefix;
+            this.prefixLength == linkAddress.prefixLength;
     }
 
     /**
@@ -98,8 +98,8 @@
     /**
      * Get network prefix length
      */
-    public int getNetworkPrefix() {
-        return prefix;
+    public int getNetworkPrefixLength() {
+        return prefixLength;
     }
 
     /**
@@ -118,7 +118,7 @@
         if (address != null) {
             dest.writeByte((byte)1);
             dest.writeByteArray(address.getAddress());
-            dest.writeInt(prefix);
+            dest.writeInt(prefixLength);
         } else {
             dest.writeByte((byte)0);
         }
@@ -132,14 +132,14 @@
         new Creator<LinkAddress>() {
             public LinkAddress createFromParcel(Parcel in) {
                 InetAddress address = null;
-                int prefix = 0;
+                int prefixLength = 0;
                 if (in.readByte() == 1) {
                     try {
                         address = InetAddress.getByAddress(in.createByteArray());
-                        prefix = in.readInt();
+                        prefixLength = in.readInt();
                     } catch (UnknownHostException e) { }
                 }
-                return new LinkAddress(address, prefix);
+                return new LinkAddress(address, prefixLength);
             }
 
             public LinkAddress[] newArray(int size) {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 01004c2..6b1fe99 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -146,6 +146,57 @@
     }
 
     /**
+     * Convert a IPv4 address from an InetAddress to an integer
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as an integer in network byte order
+     */
+    public static int inetAddressToInt(InetAddress inetAddr)
+            throws IllegalArgumentException {
+        byte [] addr = inetAddr.getAddress();
+        if (addr.length != 4) {
+            throw new IllegalArgumentException("Not an IPv4 address");
+        }
+        return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
+                ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer
+     * @param prefixLength
+     * @return the IPv4 netmask as an integer in network byte order
+     */
+    public static int prefixLengthToNetmaskInt(int prefixLength)
+            throws IllegalArgumentException {
+        if (prefixLength < 0 || prefixLength > 32) {
+            throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
+        }
+        int value = 0xffffffff << (32 - prefixLength);
+        return Integer.reverseBytes(value);
+    }
+
+    public static boolean isIpAddress(String address) {
+        //TODO: Add NetworkUtils support for IPv6 configuration and
+        //remove IPv4 validation and use a generic InetAddress validation
+        try {
+            String[] parts = address.split("\\.");
+            if (parts.length != 4) {
+                return false;
+            }
+            int a = Integer.parseInt(parts[0]);
+            if (a < 0 || a > 255) return false;
+            a = Integer.parseInt(parts[1]);
+            if (a < 0 || a > 255) return false;
+            a = Integer.parseInt(parts[2]);
+            if (a < 0 || a > 255) return false;
+            a = Integer.parseInt(parts[3]);
+            if (a < 0 || a > 255) return false;
+        } catch (NumberFormatException ex) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Add a default route through the specified gateway.
      * @param interfaceName interface on which the route should be added
      * @param gw the IP address of the gateway to which the route is desired,
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 9634157..be5fab4 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -20,6 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.DhcpInfo;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
 import android.net.ProxyProperties;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -34,13 +37,18 @@
 import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.EOFException;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -61,13 +69,10 @@
  *       ..
  *
  *       (key, value) pairs for a given network are grouped together and can
- *       be in any order. A "EOS" at the end of a set of (key, value) pairs
+ *       be in any order. A EOS at the end of a set of (key, value) pairs
  *       indicates that the next set of (key, value) pairs are for a new
- *       network. A network is identified by a unique "id". If there is no
- *       "id" key in the (key, value) pairs, the data is discarded. An IP
- *       configuration includes the keys - "ipAssignment", "ipAddress", "gateway",
- *       "netmask", "dns1" and "dns2". A proxy configuration includes "proxySettings",
- *       "proxyHost", "proxyPort" and "exclusionList"
+ *       network. A network is identified by a unique ID_KEY. If there is no
+ *       ID_KEY in the (key, value) pairs, the data is discarded.
  *
  *       An invalid version on read would result in discarding the contents of
  *       the file. On the next write, the latest version is written to file.
@@ -118,6 +123,18 @@
 
     private static final int IPCONFIG_FILE_VERSION = 1;
 
+    /* IP and proxy configuration keys */
+    private static final String ID_KEY = "id";
+    private static final String IP_ASSIGNMENT_KEY = "ipAssignment";
+    private static final String LINK_ADDRESS_KEY = "linkAddress";
+    private static final String GATEWAY_KEY = "gateway";
+    private static final String DNS_KEY = "dns";
+    private static final String PROXY_SETTINGS_KEY = "proxySettings";
+    private static final String PROXY_HOST_KEY = "proxyHost";
+    private static final String PROXY_PORT_KEY = "proxyPort";
+    private static final String EXCLUSION_LIST_KEY = "exclusionList";
+    private static final String EOS = "eos";
+
     /**
      * Initialize context, fetch the list of configured networks
      * and enable all stored networks in supplicant.
@@ -370,25 +387,61 @@
     }
 
     /**
-     * Fetch the IP configuration for a given network id
+     * Fetch the link properties for a given network id
      */
-    static DhcpInfo getIpConfiguration(int netId) {
+    static LinkProperties getLinkProperties(int netId) {
         synchronized (sConfiguredNetworks) {
             WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null) return new DhcpInfo(config.ipConfig);
+            if (config != null) return new LinkProperties(config.linkProperties);
         }
         return null;
     }
 
     /**
+     * get IP configuration for a given network id
+     * TODO: We cannot handle IPv6 addresses for configuration
+     *       right now until NetworkUtils is fixed. When we do
+     *       that, we should remove handling DhcpInfo and move
+     *       to using LinkProperties
+     */
+    static DhcpInfo getIpConfiguration(int netId) {
+        DhcpInfo dhcpInfo = new DhcpInfo();
+        LinkProperties linkProperties = getLinkProperties(netId);
+
+        if (linkProperties != null) {
+            Iterator<LinkAddress> iter = linkProperties.getLinkAddresses().iterator();
+            if (iter.hasNext()) {
+                try {
+                    LinkAddress linkAddress = iter.next();
+                    dhcpInfo.ipAddress = NetworkUtils.inetAddressToInt(
+                            linkAddress.getAddress());
+                    dhcpInfo.gateway = NetworkUtils.inetAddressToInt(
+                            linkProperties.getGateway());
+                    dhcpInfo.netmask = NetworkUtils.prefixLengthToNetmaskInt(
+                            linkAddress.getNetworkPrefixLength());
+                    Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
+                    dhcpInfo.dns1 = NetworkUtils.inetAddressToInt(dnsIterator.next());
+                    if (dnsIterator.hasNext()) {
+                        dhcpInfo.dns2 = NetworkUtils.inetAddressToInt(dnsIterator.next());
+                    }
+                } catch (IllegalArgumentException e1) {
+                    Log.e(TAG, "IPv6 address cannot be handled " + e1);
+                } catch (NullPointerException e2) {
+                    /* Should not happen since a stored static config should be valid */
+                    Log.e(TAG, "Invalid partial IP configuration " + e2);
+                }
+            }
+        }
+        return dhcpInfo;
+    }
+
+    /**
      * Fetch the proxy properties for a given network id
      */
     static ProxyProperties getProxyProperties(int netId) {
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null && config.proxySettings == ProxySettings.STATIC) {
-                return new ProxyProperties(config.proxyProperties);
-            }
+        LinkProperties linkProperties = getLinkProperties(netId);
+        if (linkProperties != null) {
+            return new ProxyProperties(linkProperties.getHttpProxy());
         }
         return null;
     }
@@ -484,71 +537,75 @@
                 for(WifiConfiguration config : sConfiguredNetworks.values()) {
                     boolean writeToFile = false;
 
-                    switch (config.ipAssignment) {
-                        case STATIC:
-                            out.writeUTF("ipAssignment");
-                            out.writeUTF(config.ipAssignment.toString());
-                            out.writeUTF("ipAddress");
-                            out.writeInt(config.ipConfig.ipAddress);
-                            out.writeUTF("gateway");
-                            out.writeInt(config.ipConfig.gateway);
-                            out.writeUTF("netmask");
-                            out.writeInt(config.ipConfig.netmask);
-                            out.writeUTF("dns1");
-                            out.writeInt(config.ipConfig.dns1);
-                            out.writeUTF("dns2");
-                            out.writeInt(config.ipConfig.dns2);
-                            writeToFile = true;
-                            break;
-                        case DHCP:
-                            out.writeUTF("ipAssignment");
-                            out.writeUTF(config.ipAssignment.toString());
-                            writeToFile = true;
-                            break;
-                        case UNASSIGNED:
-                            /* Ignore */
-                            break;
-                        default:
-                            Log.e(TAG, "Ignore invalid ip assignment while writing");
-                            break;
-                    }
-
-                    switch (config.proxySettings) {
-                        case STATIC:
-                            out.writeUTF("proxySettings");
-                            out.writeUTF(config.proxySettings.toString());
-                            InetSocketAddress proxy = config.proxyProperties.getSocketAddress();
-                            if (proxy != null) {
-                                out.writeUTF("proxyHost");
-                                out.writeUTF(proxy.getHostName());
-                                out.writeUTF("proxyPort");
-                                out.writeInt(proxy.getPort());
-                                String exclusionList = config.proxyProperties.getExclusionList();
-                                if (exclusionList != null && exclusionList.length() > 0) {
-                                    out.writeUTF("exclusionList");
-                                    out.writeUTF(exclusionList);
+                    try {
+                        LinkProperties linkProperties = config.linkProperties;
+                        switch (config.ipAssignment) {
+                            case STATIC:
+                                out.writeUTF(IP_ASSIGNMENT_KEY);
+                                out.writeUTF(config.ipAssignment.toString());
+                                for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
+                                    out.writeUTF(LINK_ADDRESS_KEY);
+                                    out.writeUTF(linkAddr.getAddress().getHostAddress());
+                                    out.writeInt(linkAddr.getNetworkPrefixLength());
                                 }
-                            }
-                            writeToFile = true;
-                            break;
-                        case NONE:
-                            out.writeUTF("proxySettings");
-                            out.writeUTF(config.proxySettings.toString());
-                            writeToFile = true;
-                            break;
-                        case UNASSIGNED:
-                            /* Ignore */
-                            break;
-                        default:
-                            Log.e(TAG, "Ignore invalid proxy settings while writing");
-                            break;
-                    }
+                                InetAddress gateway = linkProperties.getGateway();
+                                if (gateway != null) {
+                                    out.writeUTF(GATEWAY_KEY);
+                                    out.writeUTF(gateway.getHostAddress());
+                                }
+                                for (InetAddress inetAddr : linkProperties.getDnses()) {
+                                    out.writeUTF(DNS_KEY);
+                                    out.writeUTF(inetAddr.getHostAddress());
+                                }
+                                writeToFile = true;
+                                break;
+                            case DHCP:
+                                out.writeUTF(IP_ASSIGNMENT_KEY);
+                                out.writeUTF(config.ipAssignment.toString());
+                                writeToFile = true;
+                                break;
+                            case UNASSIGNED:
+                                /* Ignore */
+                                break;
+                            default:
+                                Log.e(TAG, "Ignore invalid ip assignment while writing");
+                                break;
+                        }
 
-                    if (writeToFile) {
-                        out.writeUTF("id");
-                        out.writeInt(configKey(config));
-                        out.writeUTF("EOS");
+                        switch (config.proxySettings) {
+                            case STATIC:
+                                ProxyProperties proxyProperties = linkProperties.getHttpProxy();
+                                String exclusionList = proxyProperties.getExclusionList();
+                                out.writeUTF(PROXY_SETTINGS_KEY);
+                                out.writeUTF(config.proxySettings.toString());
+                                out.writeUTF(PROXY_HOST_KEY);
+                                out.writeUTF(proxyProperties.getSocketAddress().getHostName());
+                                out.writeUTF(PROXY_PORT_KEY);
+                                out.writeInt(proxyProperties.getSocketAddress().getPort());
+                                out.writeUTF(EXCLUSION_LIST_KEY);
+                                out.writeUTF(exclusionList);
+                                writeToFile = true;
+                                break;
+                            case NONE:
+                                out.writeUTF(PROXY_SETTINGS_KEY);
+                                out.writeUTF(config.proxySettings.toString());
+                                writeToFile = true;
+                                break;
+                            case UNASSIGNED:
+                                /* Ignore */
+                                break;
+                            default:
+                                Log.e(TAG, "Ignore invalid proxy settings while writing");
+                                break;
+                        }
+                        if (writeToFile) {
+                            out.writeUTF(ID_KEY);
+                            out.writeInt(configKey(config));
+                        }
+                    } catch (NullPointerException e) {
+                        Log.e(TAG, "Failure in writing " + config.linkProperties + e);
                     }
+                    out.writeUTF(EOS);
                 }
             }
 
@@ -578,8 +635,8 @@
             while (true) {
                 int id = -1;
                 IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
-                DhcpInfo ipConfig = new DhcpInfo();
                 ProxySettings proxySettings = ProxySettings.UNASSIGNED;
+                LinkProperties linkProperties = new LinkProperties();
                 String proxyHost = null;
                 int proxyPort = -1;
                 String exclusionList = null;
@@ -587,32 +644,34 @@
 
                 do {
                     key = in.readUTF();
-                    if (key.equals("id")) {
-                        id = in.readInt();
-                    } else if (key.equals("ipAssignment")) {
-                        ipAssignment = IpAssignment.valueOf(in.readUTF());
-                    } else if (key.equals("ipAddress")) {
-                        ipConfig.ipAddress = in.readInt();
-                    } else if (key.equals("gateway")) {
-                        ipConfig.gateway = in.readInt();
-                    } else if (key.equals("netmask")) {
-                        ipConfig.netmask = in.readInt();
-                    } else if (key.equals("dns1")) {
-                        ipConfig.dns1 = in.readInt();
-                    } else if (key.equals("dns2")) {
-                        ipConfig.dns2 = in.readInt();
-                    } else if (key.equals("proxySettings")) {
-                        proxySettings = ProxySettings.valueOf(in.readUTF());
-                    } else if (key.equals("proxyHost")) {
-                        proxyHost = in.readUTF();
-                    } else if (key.equals("proxyPort")) {
-                        proxyPort = in.readInt();
-                    } else if (key.equals("exclusionList")) {
-                        exclusionList = in.readUTF();
-                    } else if (key.equals("EOS")) {
-                        break;
-                    } else {
-                        Log.e(TAG, "Ignore unknown key " + key + "while reading");
+                    try {
+                        if (key.equals(ID_KEY)) {
+                            id = in.readInt();
+                        } else if (key.equals(IP_ASSIGNMENT_KEY)) {
+                            ipAssignment = IpAssignment.valueOf(in.readUTF());
+                        } else if (key.equals(LINK_ADDRESS_KEY)) {
+                            LinkAddress linkAddr = new LinkAddress(InetAddress.getByName(
+                                    in.readUTF()), in.readInt());
+                            linkProperties.addLinkAddress(linkAddr);
+                        } else if (key.equals(GATEWAY_KEY)) {
+                            linkProperties.setGateway(InetAddress.getByName(in.readUTF()));
+                        } else if (key.equals(DNS_KEY)) {
+                            linkProperties.addDns(InetAddress.getByName(in.readUTF()));
+                        } else if (key.equals(PROXY_SETTINGS_KEY)) {
+                            proxySettings = ProxySettings.valueOf(in.readUTF());
+                        } else if (key.equals(PROXY_HOST_KEY)) {
+                            proxyHost = in.readUTF();
+                        } else if (key.equals(PROXY_PORT_KEY)) {
+                            proxyPort = in.readInt();
+                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
+                            exclusionList = in.readUTF();
+                        } else if (key.equals(EOS)) {
+                            break;
+                        } else {
+                            Log.e(TAG, "Ignore unknown key " + key + "while reading");
+                        }
+                    } catch (UnknownHostException e) {
+                        Log.e(TAG, "Ignore invalid address while reading" + e);
                     }
                 } while (true);
 
@@ -624,11 +683,9 @@
                         if (config == null) {
                             Log.e(TAG, "configuration found for missing network, ignored");
                         } else {
+                            config.linkProperties = linkProperties;
                             switch (ipAssignment) {
                                 case STATIC:
-                                    config.ipAssignment = ipAssignment;
-                                    config.ipConfig = ipConfig;
-                                    break;
                                 case DHCP:
                                     config.ipAssignment = ipAssignment;
                                     break;
@@ -647,7 +704,7 @@
                                     proxyProperties.setSocketAddress(
                                             new InetSocketAddress(proxyHost, proxyPort));
                                     proxyProperties.setExclusionList(exclusionList);
-                                    config.proxyProperties = proxyProperties;
+                                    linkProperties.setHttpProxy(proxyProperties);
                                     break;
                                 case NONE:
                                     config.proxySettings = proxySettings;
@@ -662,11 +719,12 @@
                         }
                     }
                 } else {
-                    Log.e(TAG,"Missing id while parsing configuration");
+                    Log.e(TAG, "Missing id while parsing configuration");
                 }
             }
+        } catch (EOFException ignore) {
         } catch (IOException e) {
-            Log.e(TAG, "Error parsing configuration");
+            Log.e(TAG, "Error parsing configuration" + e);
         } finally {
             if (in != null) {
                 try {
@@ -894,63 +952,140 @@
     /* Compare current and new configuration and write to file on change */
     private static void writeIpAndProxyConfigurationsOnChange(WifiConfiguration currentConfig,
             WifiConfiguration newConfig) {
-        boolean newNetwork = (newConfig.networkId == INVALID_NETWORK_ID);
-        boolean writeConfigToFile = false;
+        boolean ipChanged = false;
+        boolean proxyChanged = false;
+        LinkProperties linkProperties = new LinkProperties();
 
-        if (newConfig.ipAssignment != IpAssignment.UNASSIGNED) {
-            if (newNetwork ||
-                    (currentConfig.ipAssignment != newConfig.ipAssignment) ||
-                    (currentConfig.ipConfig.ipAddress != newConfig.ipConfig.ipAddress) ||
-                    (currentConfig.ipConfig.gateway != newConfig.ipConfig.gateway) ||
-                    (currentConfig.ipConfig.netmask != newConfig.ipConfig.netmask) ||
-                    (currentConfig.ipConfig.dns1 != newConfig.ipConfig.dns1) ||
-                    (currentConfig.ipConfig.dns2 != newConfig.ipConfig.dns2)) {
-                currentConfig.ipAssignment = newConfig.ipAssignment;
-                currentConfig.ipConfig = newConfig.ipConfig;
-                writeConfigToFile = true;
+        switch (newConfig.ipAssignment) {
+            case STATIC:
+                Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
+                        .getLinkAddresses();
+                Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
+                        .getLinkAddresses();
+                Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
+                Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
+                InetAddress currentGateway = currentConfig.linkProperties.getGateway();
+                InetAddress newGateway = newConfig.linkProperties.getGateway();
+
+                boolean linkAddressesDiffer = !currentLinkAddresses.containsAll(newLinkAddresses) ||
+                        (currentLinkAddresses.size() != newLinkAddresses.size());
+                boolean dnsesDiffer = !currentDnses.containsAll(newDnses) ||
+                        (currentDnses.size() != newDnses.size());
+                boolean gatewaysDiffer = (currentGateway == null) ||
+                        !currentGateway.equals(newGateway);
+
+                if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
+                        linkAddressesDiffer ||
+                        dnsesDiffer ||
+                        gatewaysDiffer) {
+                    ipChanged = true;
+                }
+                break;
+            case DHCP:
+                if (currentConfig.ipAssignment != newConfig.ipAssignment) {
+                    ipChanged = true;
+                }
+                break;
+            case UNASSIGNED:
+                /* Ignore */
+                break;
+            default:
+                Log.e(TAG, "Ignore invalid ip assignment during write");
+                break;
+        }
+
+        switch (newConfig.proxySettings) {
+            case STATIC:
+                InetSocketAddress newSockAddr = null;
+                String newExclusionList = null;
+                InetSocketAddress currentSockAddr = null;
+                String currentExclusionList = null;
+
+                ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
+                if (newHttpProxy != null) {
+                    newSockAddr = newHttpProxy.getSocketAddress();
+                    newExclusionList = newHttpProxy.getExclusionList();
+                }
+
+                ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
+                if (currentHttpProxy != null) {
+                    currentSockAddr =  currentHttpProxy.getSocketAddress();
+                    currentExclusionList = currentHttpProxy.getExclusionList();
+                }
+
+                boolean socketAddressDiffers = false;
+                boolean exclusionListDiffers = false;
+
+                if (newSockAddr != null && currentSockAddr != null ) {
+                    socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
+                } else if (newSockAddr != null || currentSockAddr != null) {
+                    socketAddressDiffers = true;
+                }
+
+                if (newExclusionList != null && currentExclusionList != null) {
+                    exclusionListDiffers = !currentExclusionList.equals(newExclusionList);
+                } else if (newExclusionList != null || currentExclusionList != null) {
+                    exclusionListDiffers = true;
+                }
+
+                if ((currentConfig.proxySettings != newConfig.proxySettings) ||
+                        socketAddressDiffers ||
+                        exclusionListDiffers) {
+                    proxyChanged = true;
+                }
+                break;
+            case NONE:
+                if (currentConfig.proxySettings != newConfig.proxySettings) {
+                    proxyChanged = true;
+                }
+                break;
+            case UNASSIGNED:
+                /* Ignore */
+                break;
+            default:
+                Log.e(TAG, "Ignore invalid proxy configuration during write");
+                break;
+        }
+
+        if (!ipChanged) {
+            addIpSettingsFromConfig(linkProperties, currentConfig);
+        } else {
+            currentConfig.ipAssignment = newConfig.ipAssignment;
+            addIpSettingsFromConfig(linkProperties, newConfig);
+            Log.d(TAG, "IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
+                    linkProperties.toString());
+        }
+
+
+        if (!proxyChanged) {
+            linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
+        } else {
+            currentConfig.proxySettings = newConfig.proxySettings;
+            linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
+            Log.d(TAG, "proxy changed SSID = " + currentConfig.SSID);
+            if (linkProperties.getHttpProxy() != null) {
+                Log.d(TAG, " proxyProperties: " + linkProperties.getHttpProxy().toString());
             }
         }
 
-        if (newConfig.proxySettings != ProxySettings.UNASSIGNED) {
-            InetSocketAddress newSockAddr = newConfig.proxyProperties.getSocketAddress();
-            String newExclusionList = newConfig.proxyProperties.getExclusionList();
-
-            InetSocketAddress currentSockAddr = currentConfig.proxyProperties.getSocketAddress();
-            String currentExclusionList = currentConfig.proxyProperties.getExclusionList();
-
-            boolean socketAddressDiffers = false;
-            boolean exclusionListDiffers = false;
-
-            if (newSockAddr != null && currentSockAddr != null ) {
-                socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
-            } else if (newSockAddr != null || currentSockAddr != null) {
-                socketAddressDiffers = true;
-            }
-
-            if (newExclusionList != null && currentExclusionList != null) {
-                exclusionListDiffers = currentExclusionList.equals(newExclusionList);
-            } else if (newExclusionList != null || currentExclusionList != null) {
-                exclusionListDiffers = true;
-            }
-
-            if (newNetwork ||
-                    (currentConfig.proxySettings != newConfig.proxySettings) ||
-                    socketAddressDiffers ||
-                    exclusionListDiffers) {
-                currentConfig.proxySettings = newConfig.proxySettings;
-                currentConfig.proxyProperties = newConfig.proxyProperties;
-                Log.d(TAG, "proxy change SSID = " + currentConfig.SSID + " proxyProperties: " +
-                        currentConfig.proxyProperties.toString());
-                writeConfigToFile = true;
-            }
-        }
-
-        if (writeConfigToFile) {
+        if (ipChanged || proxyChanged) {
+            currentConfig.linkProperties = linkProperties;
             writeIpAndProxyConfigurations();
             sendConfigChangeBroadcast();
         }
     }
 
+    private static void addIpSettingsFromConfig(LinkProperties linkProperties,
+            WifiConfiguration config) {
+        for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
+            linkProperties.addLinkAddress(linkAddr);
+        }
+        linkProperties.setGateway(config.linkProperties.getGateway());
+        for (InetAddress dns : config.linkProperties.getDnses()) {
+            linkProperties.addDns(dns);
+        }
+    }
+
     /**
      * Read the variables from the supplicant daemon that are needed to
      * fill in the WifiConfiguration object.
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c4a1310..d5fb63e 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,8 +16,7 @@
 
 package android.net.wifi;
 
-import android.net.DhcpInfo;
-import android.net.ProxyProperties;
+import android.net.LinkProperties;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -303,7 +302,7 @@
      */
     public enum IpAssignment {
         /* Use statically configured IP settings. Configuration can be accessed
-         * with ipConfig */
+         * with linkProperties */
         STATIC,
         /* Use dynamically configured IP settigns */
         DHCP,
@@ -315,10 +314,6 @@
      * @hide
      */
     public IpAssignment ipAssignment;
-    /**
-     * @hide
-     */
-    public DhcpInfo ipConfig;
 
     /**
      * @hide
@@ -328,7 +323,7 @@
          * should be cleared. */
         NONE,
         /* Use statically configured proxy. Configuration can be accessed
-         * with proxyProperties */
+         * with linkProperties */
         STATIC,
         /* no proxy details are assigned, this is used to indicate
          * that any existing proxy settings should be retained */
@@ -341,7 +336,7 @@
     /**
      * @hide
      */
-    public ProxyProperties proxyProperties;
+    public LinkProperties linkProperties;
 
     public WifiConfiguration() {
         networkId = INVALID_NETWORK_ID;
@@ -361,9 +356,8 @@
             field.setValue(null);
         }
         ipAssignment = IpAssignment.UNASSIGNED;
-        ipConfig = new DhcpInfo();
         proxySettings = ProxySettings.UNASSIGNED;
-        proxyProperties = new ProxyProperties();
+        linkProperties = new LinkProperties();
     }
 
     public String toString() {
@@ -445,17 +439,13 @@
             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');
+        sbuf.append("IP assignment: " + ipAssignment.toString());
+        sbuf.append("\n");
+        sbuf.append("Proxy settings: " + proxySettings.toString());
+        sbuf.append("\n");
+        sbuf.append(linkProperties.toString());
+        sbuf.append("\n");
 
-        if (proxySettings == ProxySettings.STATIC) {
-            sbuf.append(" ").append("Proxy configuration:").append('\n');
-            sbuf.append(" ").append(proxyProperties);
-        }
-        sbuf.append('\n');
         return sbuf.toString();
     }
 
@@ -521,9 +511,8 @@
                 enterpriseFields[i].setValue(source.enterpriseFields[i].value());
             }
             ipAssignment = source.ipAssignment;
-            ipConfig = new DhcpInfo(source.ipConfig);
             proxySettings = source.proxySettings;
-            proxyProperties = new ProxyProperties(source.proxyProperties);
+            linkProperties = new LinkProperties(source.linkProperties);
         }
     }
 
@@ -550,15 +539,8 @@
             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);
         dest.writeString(proxySettings.name());
-        dest.writeParcelable(proxyProperties, flags);
+        dest.writeParcelable(linkProperties, flags);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -587,15 +569,8 @@
                 }
 
                 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();
                 config.proxySettings = ProxySettings.valueOf(in.readString());
-                config.proxyProperties = in.readParcelable(null);
+                config.linkProperties = in.readParcelable(null);
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 572abc0..efd0e0a 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1253,25 +1253,22 @@
     }
 
     private void configureLinkProperties() {
-
+        if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+            mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
+        } else {
+            // TODO - fix this for v6
+            synchronized (mDhcpInfo) {
+                mLinkProperties.addLinkAddress(new LinkAddress(
+                        NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress),
+                        NetworkUtils.intToInetAddress(mDhcpInfo.netmask)));
+                mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
+                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
+                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
+            }
+            mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+        }
         mLinkProperties.setInterfaceName(mInterfaceName);
-
-        // TODO - fix this for v6
-        synchronized (mDhcpInfo) {
-            mLinkProperties.addLinkAddress(new LinkAddress(
-                    NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress),
-                    NetworkUtils.intToInetAddress(mDhcpInfo.netmask)));
-            mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
-            mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
-            mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
-        }
-
-        ProxyProperties proxyProperties = WifiConfigStore.getProxyProperties(mLastNetworkId);
-        if (proxyProperties != null) {
-            mLinkProperties.setHttpProxy(proxyProperties);
-            Log.d(TAG, "netId=" + mLastNetworkId  + " proxy configured: "
-                    + proxyProperties.toString());
-        }
+        Log.d(TAG, "netId=" + mLastNetworkId  + " Link configured: " + mLinkProperties.toString());
     }
 
     private int getMaxDhcpRetries() {
@@ -2571,7 +2568,6 @@
                   mLastSignalLevel = -1; // force update of signal strength
                   synchronized (mDhcpInfo) {
                       mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
-                      Log.d(TAG, "IP configuration: " + mDhcpInfo);
                   }
                   configureLinkProperties();
                   setDetailedState(DetailedState.CONNECTED);