Merge "Move IP config from WifiConfiguration to IpConfiguration."
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
new file mode 100644
index 0000000..4730bab
--- /dev/null
+++ b/core/java/android/net/IpConfiguration.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 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 android.net.LinkProperties;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A class representing a configured network.
+ * @hide
+ */
+public class IpConfiguration implements Parcelable {
+    private static final String TAG = "IpConfiguration";
+
+    public enum IpAssignment {
+        /* Use statically configured IP settings. Configuration can be accessed
+         * with linkProperties */
+        STATIC,
+        /* Use dynamically configured IP settigns */
+        DHCP,
+        /* no IP details are assigned, this is used to indicate
+         * that any existing IP settings should be retained */
+        UNASSIGNED
+    }
+
+    public IpAssignment ipAssignment;
+
+    public enum ProxySettings {
+        /* No proxy is to be used. Any existing proxy settings
+         * should be cleared. */
+        NONE,
+        /* Use statically configured proxy. Configuration can be accessed
+         * with linkProperties */
+        STATIC,
+        /* no proxy details are assigned, this is used to indicate
+         * that any existing proxy settings should be retained */
+        UNASSIGNED,
+        /* Use a Pac based proxy.
+         */
+        PAC
+    }
+
+    public ProxySettings proxySettings;
+
+    public LinkProperties linkProperties;
+
+    public IpConfiguration(IpConfiguration source) {
+        if (source != null) {
+            ipAssignment = source.ipAssignment;
+            proxySettings = source.proxySettings;
+            linkProperties = new LinkProperties(source.linkProperties);
+        } else {
+            ipAssignment = IpAssignment.UNASSIGNED;
+            proxySettings = ProxySettings.UNASSIGNED;
+            linkProperties = new LinkProperties();
+        }
+    }
+
+    public IpConfiguration() {
+         this(null);
+    }
+
+    public IpConfiguration(IpAssignment ipAssignment,
+                           ProxySettings proxySettings,
+                           LinkProperties linkProperties) {
+        this.ipAssignment = ipAssignment;
+        this.proxySettings = proxySettings;
+        this.linkProperties = new LinkProperties(linkProperties);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        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");
+
+        return sbuf.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof IpConfiguration)) {
+            return false;
+        }
+
+        IpConfiguration other = (IpConfiguration) o;
+        return this.ipAssignment == other.ipAssignment &&
+                this.proxySettings == other.proxySettings &&
+                Objects.equals(this.linkProperties, other.linkProperties);
+    }
+
+    @Override
+    public int hashCode() {
+        return 13 + (linkProperties != null ? linkProperties.hashCode() : 0) +
+               17 * ipAssignment.ordinal() +
+               47 * proxySettings.ordinal();
+    }
+
+    /** Implement the Parcelable interface */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface  */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(ipAssignment.name());
+        dest.writeString(proxySettings.name());
+        dest.writeParcelable(linkProperties, flags);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<IpConfiguration> CREATOR =
+        new Creator<IpConfiguration>() {
+            public IpConfiguration createFromParcel(Parcel in) {
+                IpConfiguration config = new IpConfiguration();
+                config.ipAssignment = IpAssignment.valueOf(in.readString());
+                config.proxySettings = ProxySettings.valueOf(in.readString());
+                config.linkProperties = in.readParcelable(null);
+                return config;
+            }
+
+            public IpConfiguration[] newArray(int size) {
+                return new IpConfiguration[size];
+            }
+        };
+}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index cad030a..2ce87eb 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -23,15 +23,15 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.AuthAlgorithm;
-import android.net.wifi.WifiConfiguration.IpAssignment;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiConfiguration.ProxySettings;
-import android.net.wifi.WifiEnterpriseConfig;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiEnterpriseConfig;
 
 import java.io.InputStream;
 import java.net.InetAddress;
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
index 91c3093..9692ec0 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java
@@ -18,12 +18,12 @@
 
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
 import android.net.NetworkInfo.State;
 import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.PowerManager;
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/services/core/java/com/android/server/net/DelayedDiskWrite.java
new file mode 100644
index 0000000..6ed277d
--- /dev/null
+++ b/services/core/java/com/android/server/net/DelayedDiskWrite.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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 com.android.server.net;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class DelayedDiskWrite {
+    private HandlerThread mDiskWriteHandlerThread;
+    private Handler mDiskWriteHandler;
+    /* Tracks multiple writes on the same thread */
+    private int mWriteSequence = 0;
+    private final String TAG = "DelayedDiskWrite";
+
+    public interface Writer {
+        public void onWriteCalled(DataOutputStream out) throws IOException;
+    }
+
+    public void write(final String filePath, final Writer w) {
+        if (TextUtils.isEmpty(filePath)) {
+            throw new IllegalArgumentException("empty file path");
+        }
+
+        /* Do a delayed write to disk on a separate handler thread */
+        synchronized (this) {
+            if (++mWriteSequence == 1) {
+                mDiskWriteHandlerThread = new HandlerThread("DelayedDiskWriteThread");
+                mDiskWriteHandlerThread.start();
+                mDiskWriteHandler = new Handler(mDiskWriteHandlerThread.getLooper());
+            }
+        }
+
+        mDiskWriteHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                doWrite(filePath, w);
+            }
+        });
+    }
+
+    private void doWrite(String filePath, Writer w) {
+        DataOutputStream out = null;
+        try {
+            out = new DataOutputStream(new BufferedOutputStream(
+                        new FileOutputStream(filePath)));
+            w.onWriteCalled(out);
+        } catch (IOException e) {
+            loge("Error writing data file " + filePath);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (Exception e) {}
+            }
+
+            // Quit if no more writes sent
+            synchronized (this) {
+                if (--mWriteSequence == 0) {
+                    mDiskWriteHandler.getLooper().quit();
+                    mDiskWriteHandler = null;
+                    mDiskWriteHandlerThread = null;
+                }
+            }
+        }
+    }
+
+    private void loge(String s) {
+        Log.e(TAG, s);
+    }
+}
+
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
new file mode 100644
index 0000000..6ccbec0
--- /dev/null
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2014 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 com.android.server.net;
+
+import android.net.IpConfiguration;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.ProxyInfo;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.net.DelayedDiskWrite;
+
+import java.io.BufferedInputStream;
+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.util.Map;
+
+public class IpConfigStore {
+    private static final String TAG = "IpConfigStore";
+    private static final boolean DBG = true;
+
+    protected final DelayedDiskWrite mWriter;
+
+    /* IP and proxy configuration keys */
+    protected static final String ID_KEY = "id";
+    protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";
+    protected static final String LINK_ADDRESS_KEY = "linkAddress";
+    protected static final String GATEWAY_KEY = "gateway";
+    protected static final String DNS_KEY = "dns";
+    protected static final String PROXY_SETTINGS_KEY = "proxySettings";
+    protected static final String PROXY_HOST_KEY = "proxyHost";
+    protected static final String PROXY_PORT_KEY = "proxyPort";
+    protected static final String PROXY_PAC_FILE = "proxyPac";
+    protected static final String EXCLUSION_LIST_KEY = "exclusionList";
+    protected static final String EOS = "eos";
+
+    protected static final int IPCONFIG_FILE_VERSION = 2;
+
+    public IpConfigStore() {
+        mWriter = new DelayedDiskWrite();
+    }
+
+    private boolean writeConfig(DataOutputStream out, int configKey,
+                                  IpConfiguration config) throws IOException {
+        boolean written = false;
+
+        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());
+                    }
+                    for (RouteInfo route : linkProperties.getRoutes()) {
+                        out.writeUTF(GATEWAY_KEY);
+                        LinkAddress dest = route.getDestination();
+                        if (dest != null) {
+                            out.writeInt(1);
+                            out.writeUTF(dest.getAddress().getHostAddress());
+                            out.writeInt(dest.getNetworkPrefixLength());
+                        } else {
+                            out.writeInt(0);
+                        }
+                        if (route.getGateway() != null) {
+                            out.writeInt(1);
+                            out.writeUTF(route.getGateway().getHostAddress());
+                        } else {
+                            out.writeInt(0);
+                        }
+                    }
+                    for (InetAddress inetAddr : linkProperties.getDnses()) {
+                        out.writeUTF(DNS_KEY);
+                        out.writeUTF(inetAddr.getHostAddress());
+                    }
+                    written = true;
+                    break;
+                case DHCP:
+                    out.writeUTF(IP_ASSIGNMENT_KEY);
+                    out.writeUTF(config.ipAssignment.toString());
+                    written = true;
+                    break;
+                case UNASSIGNED:
+                /* Ignore */
+                    break;
+                default:
+                    loge("Ignore invalid ip assignment while writing");
+                    break;
+            }
+
+            switch (config.proxySettings) {
+                case STATIC:
+                    ProxyInfo proxyProperties = linkProperties.getHttpProxy();
+                    String exclusionList = proxyProperties.getExclusionListAsString();
+                    out.writeUTF(PROXY_SETTINGS_KEY);
+                    out.writeUTF(config.proxySettings.toString());
+                    out.writeUTF(PROXY_HOST_KEY);
+                    out.writeUTF(proxyProperties.getHost());
+                    out.writeUTF(PROXY_PORT_KEY);
+                    out.writeInt(proxyProperties.getPort());
+                    out.writeUTF(EXCLUSION_LIST_KEY);
+                    out.writeUTF(exclusionList);
+                    written = true;
+                    break;
+                case PAC:
+                    ProxyInfo proxyPacProperties = linkProperties.getHttpProxy();
+                    out.writeUTF(PROXY_SETTINGS_KEY);
+                    out.writeUTF(config.proxySettings.toString());
+                    out.writeUTF(PROXY_PAC_FILE);
+                    out.writeUTF(proxyPacProperties.getPacFileUrl().toString());
+                    written = true;
+                    break;
+                case NONE:
+                    out.writeUTF(PROXY_SETTINGS_KEY);
+                    out.writeUTF(config.proxySettings.toString());
+                    written = true;
+                    break;
+                case UNASSIGNED:
+                    /* Ignore */
+                        break;
+                    default:
+                        loge("Ignore invalid proxy settings while writing");
+                        break;
+            }
+
+            if (written) {
+                out.writeUTF(ID_KEY);
+                out.writeInt(configKey);
+            }
+        } catch (NullPointerException e) {
+            loge("Failure in writing " + config.linkProperties + e);
+        }
+        out.writeUTF(EOS);
+
+        return written;
+    }
+
+    public void writeIpAndProxyConfigurations(String filePath,
+                                              final SparseArray<IpConfiguration> networks) {
+        mWriter.write(filePath, new DelayedDiskWrite.Writer() {
+            public void onWriteCalled(DataOutputStream out) throws IOException{
+                out.writeInt(IPCONFIG_FILE_VERSION);
+                for(int i = 0; i < networks.size(); i++) {
+                    writeConfig(out, networks.keyAt(i), networks.valueAt(i));
+                }
+            }
+        });
+    }
+
+    public SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
+        SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
+
+        DataInputStream in = null;
+        try {
+            in = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
+
+            int version = in.readInt();
+            if (version != 2 && version != 1) {
+                loge("Bad version on IP configuration file, ignore read");
+                return null;
+            }
+
+            while (true) {
+                int id = -1;
+                // Default is DHCP with no proxy
+                IpAssignment ipAssignment = IpAssignment.DHCP;
+                ProxySettings proxySettings = ProxySettings.NONE;
+                LinkProperties linkProperties = new LinkProperties();
+                String proxyHost = null;
+                String pacFileUrl = null;
+                int proxyPort = -1;
+                String exclusionList = null;
+                String key;
+
+                do {
+                    key = in.readUTF();
+                    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(
+                                    NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
+                            linkProperties.addLinkAddress(linkAddr);
+                        } else if (key.equals(GATEWAY_KEY)) {
+                            LinkAddress dest = null;
+                            InetAddress gateway = null;
+                            if (version == 1) {
+                                // only supported default gateways - leave the dest/prefix empty
+                                gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                            } else {
+                                if (in.readInt() == 1) {
+                                    dest = new LinkAddress(
+                                            NetworkUtils.numericToInetAddress(in.readUTF()),
+                                            in.readInt());
+                                }
+                                if (in.readInt() == 1) {
+                                    gateway = NetworkUtils.numericToInetAddress(in.readUTF());
+                                }
+                            }
+                            linkProperties.addRoute(new RouteInfo(dest, gateway));
+                        } else if (key.equals(DNS_KEY)) {
+                            linkProperties.addDns(
+                                    NetworkUtils.numericToInetAddress(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(PROXY_PAC_FILE)) {
+                            pacFileUrl = in.readUTF();
+                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
+                            exclusionList = in.readUTF();
+                        } else if (key.equals(EOS)) {
+                            break;
+                        } else {
+                            loge("Ignore unknown key " + key + "while reading");
+                        }
+                    } catch (IllegalArgumentException e) {
+                        loge("Ignore invalid address while reading" + e);
+                    }
+                } while (true);
+
+                if (id != -1) {
+                    IpConfiguration config = new IpConfiguration();
+                    networks.put(id, config);
+
+                    config.linkProperties = linkProperties;
+                    switch (ipAssignment) {
+                        case STATIC:
+                        case DHCP:
+                            config.ipAssignment = ipAssignment;
+                            break;
+                        case UNASSIGNED:
+                            loge("BUG: Found UNASSIGNED IP on file, use DHCP");
+                            config.ipAssignment = IpAssignment.DHCP;
+                            break;
+                        default:
+                            loge("Ignore invalid ip assignment while reading.");
+                            config.ipAssignment = IpAssignment.UNASSIGNED;
+                            break;
+                    }
+
+                    switch (proxySettings) {
+                        case STATIC:
+                            config.proxySettings = proxySettings;
+                            ProxyInfo ProxyInfo =
+                                    new ProxyInfo(proxyHost, proxyPort, exclusionList);
+                            linkProperties.setHttpProxy(ProxyInfo);
+                            break;
+                        case PAC:
+                            config.proxySettings = proxySettings;
+                            ProxyInfo proxyPacProperties =
+                                    new ProxyInfo(pacFileUrl);
+                            linkProperties.setHttpProxy(proxyPacProperties);
+                            break;
+                        case NONE:
+                            config.proxySettings = proxySettings;
+                            break;
+                        case UNASSIGNED:
+                            loge("BUG: Found UNASSIGNED proxy on file, use NONE");
+                            config.proxySettings = ProxySettings.NONE;
+                            break;
+                        default:
+                            loge("Ignore invalid proxy settings while reading");
+                            config.proxySettings = ProxySettings.UNASSIGNED;
+                            break;
+                    }
+                } else {
+                    if (DBG) log("Missing id while parsing configuration");
+                }
+            }
+        } catch (EOFException ignore) {
+        } catch (IOException e) {
+            loge("Error parsing configuration" + e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception e) {}
+            }
+        }
+
+        return networks;
+    }
+
+    protected void loge(String s) {
+        Log.e(TAG, s);
+    }
+
+    protected void log(String s) {
+        Log.d(TAG, s);
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e73cce1b..292f844 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,9 +16,13 @@
 
 package android.net.wifi;
 
+import android.net.IpConfiguration;
+import android.net.IpConfiguration.ProxySettings;
+import android.net.IpConfiguration.IpAssignment;
+import android.net.ProxyInfo;
 import android.net.LinkProperties;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
 
 import java.util.HashMap;
@@ -286,20 +290,7 @@
     /**
      * @hide
      */
-    public enum IpAssignment {
-        /* Use statically configured IP settings. Configuration can be accessed
-         * with linkProperties */
-        STATIC,
-        /* Use dynamically configured IP settigns */
-        DHCP,
-        /* no IP details are assigned, this is used to indicate
-         * that any existing IP settings should be retained */
-        UNASSIGNED
-    }
-    /**
-     * @hide
-     */
-    public IpAssignment ipAssignment;
+    private IpConfiguration mIpConfiguration;
 
     /**
      * @hide
@@ -457,32 +448,6 @@
      */
     public HashMap<String, Integer>  linkedConfigurations;
 
-    /**
-     * @hide
-     */
-    public enum ProxySettings {
-        /* No proxy is to be used. Any existing proxy settings
-         * should be cleared. */
-        NONE,
-        /* Use statically configured proxy. Configuration can be accessed
-         * with linkProperties */
-        STATIC,
-        /* no proxy details are assigned, this is used to indicate
-         * that any existing proxy settings should be retained */
-        UNASSIGNED,
-        /* Use a Pac based proxy.
-         */
-        PAC
-    }
-    /**
-     * @hide
-     */
-    public ProxySettings proxySettings;
-    /**
-     * @hide
-     */
-    public LinkProperties linkProperties;
-
     public WifiConfiguration() {
         networkId = INVALID_NETWORK_ID;
         SSID = null;
@@ -500,12 +465,10 @@
             wepKeys[i] = null;
         }
         enterpriseConfig = new WifiEnterpriseConfig();
-        ipAssignment = IpAssignment.UNASSIGNED;
-        proxySettings = ProxySettings.UNASSIGNED;
-        linkProperties = new LinkProperties();
         autoJoinStatus = AUTO_JOIN_ENABLED;
         selfAdded = false;
         ephemeral = false;
+        mIpConfiguration = new IpConfiguration();
     }
 
     /**
@@ -640,12 +603,7 @@
         sbuf.append(enterpriseConfig);
         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");
+        sbuf.append(mIpConfiguration.toString());
 
         return sbuf.toString();
     }
@@ -823,6 +781,52 @@
         return key;
     }
 
+    /** @hide */
+    public IpConfiguration getIpConfiguration() {
+        return mIpConfiguration;
+    }
+
+    /** @hide */
+    public void setIpConfiguration(IpConfiguration ipConfiguration) {
+        mIpConfiguration = ipConfiguration;
+    }
+
+    /** @hide */
+    public LinkProperties getLinkProperties() {
+        return mIpConfiguration.linkProperties;
+    }
+
+    /** @hide */
+    public void setLinkProperties(LinkProperties linkProperties) {
+        mIpConfiguration.linkProperties = linkProperties;
+    }
+
+    /** @hide */
+    public IpConfiguration.IpAssignment getIpAssignment() {
+        return mIpConfiguration.ipAssignment;
+    }
+
+    /** @hide */
+    public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
+        mIpConfiguration.ipAssignment = ipAssignment;
+    }
+
+    /** @hide */
+    public IpConfiguration.ProxySettings getProxySettings() {
+        return mIpConfiguration.proxySettings;
+    }
+
+    /** @hide */
+    public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
+        mIpConfiguration.proxySettings = proxySettings;
+    }
+
+    /** @hide */
+    public void setProxy(ProxySettings settings, ProxyInfo proxy) {
+        mIpConfiguration.proxySettings = settings;
+        mIpConfiguration.linkProperties.setHttpProxy(proxy);
+    }
+
     /** Implement the Parcelable interface {@hide} */
     public int describeContents() {
         return 0;
@@ -854,12 +858,10 @@
 
             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
 
-            ipAssignment = source.ipAssignment;
-            proxySettings = source.proxySettings;
-
             defaultGwMacAddress = source.defaultGwMacAddress;
 
-            linkProperties = new LinkProperties(source.linkProperties);
+            mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
+
             if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
                 scanResultCache = new HashMap<String, ScanResult>();
                 scanResultCache.putAll(source.scanResultCache);
@@ -882,10 +884,11 @@
             if (source.visibility != null) {
                 visibility = new Visibility(source.visibility);
             }
-       }
+        }
     }
 
     /** Implement the Parcelable interface {@hide} */
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(networkId);
         dest.writeInt(status);
@@ -908,10 +911,7 @@
 
         dest.writeParcelable(enterpriseConfig, flags);
 
-        dest.writeString(ipAssignment.name());
-        dest.writeString(proxySettings.name());
-        dest.writeParcelable(linkProperties, flags);
-
+        dest.writeParcelable(mIpConfiguration, flags);
         dest.writeString(dhcpServer);
         dest.writeString(defaultGwMacAddress);
         dest.writeInt(autoJoinStatus);
@@ -943,10 +943,7 @@
 
                 config.enterpriseConfig = in.readParcelable(null);
 
-                config.ipAssignment = IpAssignment.valueOf(in.readString());
-                config.proxySettings = ProxySettings.valueOf(in.readString());
-                config.linkProperties = in.readParcelable(null);
-
+                config.mIpConfiguration = in.readParcelable(null);
                 config.dhcpServer = in.readString();
                 config.defaultGwMacAddress = in.readString();
                 config.autoJoinStatus = in.readInt();