Merge changes I69c1931b,I6b08f52d

* changes:
  wifi: hotspot2: omadm: add XML parser for parsing OMA-DM XML string
  wifi: hotspot2: initial implementation of PasspointConfiguration
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
new file mode 100644
index 0000000..6b1cea8
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2;
+
+parcelable PasspointConfiguration;
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
new file mode 100644
index 0000000..18aae53
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Class representing Passpoint configuration.  This contains configurations specified in
+ * PerProviderSubscription (PPS) Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently, only HomeSP and Credential subtrees are supported.
+ *
+ * @hide
+ */
+public final class PasspointConfiguration implements Parcelable {
+    public HomeSP homeSp = null;
+    public Credential credential = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(homeSp, flags);
+        dest.writeParcelable(credential, flags);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof PasspointConfiguration)) {
+            return false;
+        }
+        PasspointConfiguration that = (PasspointConfiguration) thatObject;
+        return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) &&
+                (credential == null ? that.credential == null :
+                    credential.equals(that.credential));
+    }
+
+    public static final Creator<PasspointConfiguration> CREATOR =
+        new Creator<PasspointConfiguration>() {
+            @Override
+            public PasspointConfiguration createFromParcel(Parcel in) {
+                PasspointConfiguration config = new PasspointConfiguration();
+                config.homeSp = in.readParcelable(null);
+                config.credential = in.readParcelable(null);
+                return config;
+            }
+            @Override
+            public PasspointConfiguration[] newArray(int size) {
+                return new PasspointConfiguration[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
new file mode 100644
index 0000000..e87698c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2.omadm;
+
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class represent a node in an XML tree. Each node is an XML element.
+ * Used by {@link XMLParser} for parsing/converting each XML element to XMLNode.
+ *
+ * @hide
+ */
+public class XMLNode {
+    private final String mTag;
+    private final List<XMLNode> mChildren;
+    private final XMLNode mParent;
+    private StringBuilder mTextBuilder;
+    private String mText;
+
+    public XMLNode(XMLNode parent, String tag) {
+        mTag = tag;
+        mParent = parent;
+        mChildren = new ArrayList<>();
+        mTextBuilder = new StringBuilder();
+        mText = null;
+    }
+
+    /**
+     * Adding a text to this node. Invoked by {@link XMLParser#characters}.
+     *
+     * @param text String to be added
+     */
+    public void addText(String text) {
+        mTextBuilder.append(text);
+    }
+
+    /**
+     * Adding a child node to this node. Invoked by {@link XMLParser#startElement}.
+     *
+     * @param child XMLNode to be added
+     */
+    public void addChild(XMLNode child) {
+        mChildren.add(child);
+    }
+
+    /**
+     * Invoked when the end of the XML element is detected. Used for further processing
+     * of the text enclosed within this XML element. Invoked by {@link XMLParser#endElement}.
+     */
+    public void close() {
+        // Remove the leading and the trailing whitespaces.
+        mText = mTextBuilder.toString().trim();
+        mTextBuilder = null;
+    }
+
+    public String getTag() {
+        return mTag;
+    }
+
+    public XMLNode getParent() {
+        return mParent;
+    }
+
+    public String getText() {
+        return mText;
+    }
+
+    public List<XMLNode> getChildren() {
+        return mChildren;
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof XMLNode)) {
+            return false;
+        }
+        XMLNode that = (XMLNode) thatObject;
+
+        return TextUtils.equals(mTag, that.mTag) &&
+                TextUtils.equals(mText, that.mText) &&
+                mChildren.equals(that.mChildren);
+    }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
new file mode 100644
index 0000000..948052c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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.wifi.hotspot2.omadm;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.text.TextUtils;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Class for parsing an XML string to an XML tree represented by {@link XMLNode}.
+ *
+ * The original XML string:
+ * <root>
+ *   <tag1>text1</tag1>
+ *   <tag2>
+ *     <tag3>text3</tag3>
+ *   </tag2>
+ * </root>
+ *
+ * The XML tree representation:
+ *                  [root]
+ *                     |
+ *                     |
+ *   [tag1, text1]-----|-----[tag2]
+ *                             |
+ *                             |
+ *                       [tag3, text3]
+ *
+ * @hide
+ */
+public class XMLParser extends DefaultHandler {
+    private XMLNode mRoot = null;
+    private XMLNode mCurrent = null;
+
+    public XMLNode parse(String text) throws IOException, SAXException {
+        if (TextUtils.isEmpty(text)) {
+            throw new IOException("XML string not provided");
+        }
+
+        // Reset pointers.
+        mRoot = null;
+        mCurrent = null;
+
+        try {
+            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+            parser.parse(new InputSource(new StringReader(text)), this);
+            return mRoot;
+        } catch (ParserConfigurationException pce) {
+            throw new SAXException(pce);
+        }
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes)
+            throws SAXException {
+        XMLNode parent = mCurrent;
+
+        mCurrent = new XMLNode(parent, qName);
+
+        if (mRoot == null) {
+            mRoot = mCurrent;
+        } else if (parent == null) {
+            throw new SAXException("More than one root nodes");
+        } else {
+            parent.addChild(mCurrent);
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (!qName.equals(mCurrent.getTag())) {
+            throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
+                    mCurrent);
+        }
+
+        mCurrent.close();
+        mCurrent = mCurrent.getParent();
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        mCurrent.addText(new String(ch, start, length));
+    }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
new file mode 100644
index 0000000..3d8e833
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2.pps;
+
+parcelable Credential;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
new file mode 100644
index 0000000..92dbd8a
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -0,0 +1,376 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2.pps;
+
+import android.net.wifi.ParcelUtil;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+/**
+ * Class representing Credential subtree in the PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * In addition to the fields in the Credential subtree, this will also maintain necessary
+ * information for the private key and certificates associated with this credential.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+    /**
+     * The realm associated with this credential.  It will be used to determine
+     * if this credential can be used to authenticate with a given hotspot by
+     * comparing the realm specified in that hotspot's ANQP element.
+     */
+    public String realm = null;
+
+    /**
+     * Username-password based credential.
+     * Contains the fields under PerProviderSubscription/Credential/UsernamePassword subtree.
+     */
+    public static final class UserCredential implements Parcelable {
+        /**
+         * Username of the credential.
+         */
+        public String username = null;
+
+        /**
+         * Base64-encoded password.
+         */
+        public String password = null;
+
+        /**
+         * EAP (Extensible Authentication Protocol) method type.
+         * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+         * for valid values.
+         * Using Integer.MIN_VALUE to indicate unset value.
+         */
+        public int eapType = Integer.MIN_VALUE;
+
+        /**
+         * Non-EAP inner authentication method.
+         */
+        public String nonEapInnerMethod = null;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(username);
+            dest.writeString(password);
+            dest.writeInt(eapType);
+            dest.writeString(nonEapInnerMethod);
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof UserCredential)) {
+                return false;
+            }
+
+            UserCredential that = (UserCredential) thatObject;
+            return TextUtils.equals(username, that.username) &&
+                    TextUtils.equals(password, that.password) &&
+                    eapType == that.eapType &&
+                    TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
+        }
+
+        public static final Creator<UserCredential> CREATOR =
+            new Creator<UserCredential>() {
+                @Override
+                public UserCredential createFromParcel(Parcel in) {
+                    UserCredential userCredential = new UserCredential();
+                    userCredential.username = in.readString();
+                    userCredential.password = in.readString();
+                    userCredential.eapType = in.readInt();
+                    userCredential.nonEapInnerMethod = in.readString();
+                    return userCredential;
+                }
+
+                @Override
+                public UserCredential[] newArray(int size) {
+                    return new UserCredential[size];
+                }
+            };
+    }
+    public UserCredential userCredential = null;
+
+    /**
+     * Certificate based credential.
+     * Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree.
+     */
+    public static final class CertificateCredential implements Parcelable {
+        /**
+         * Certificate type. Valid values are "802.1ar" and "x509v3".
+         */
+        public String certType = null;
+
+        /**
+         * The SHA-256 fingerprint of the certificate.
+         */
+        public byte[] certSha256FingerPrint = null;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(certType);
+            dest.writeByteArray(certSha256FingerPrint);
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof CertificateCredential)) {
+                return false;
+            }
+
+            CertificateCredential that = (CertificateCredential) thatObject;
+            return TextUtils.equals(certType, that.certType) &&
+                    Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
+        }
+
+        public static final Creator<CertificateCredential> CREATOR =
+            new Creator<CertificateCredential>() {
+                @Override
+                public CertificateCredential createFromParcel(Parcel in) {
+                    CertificateCredential certCredential = new CertificateCredential();
+                    certCredential.certType = in.readString();
+                    certCredential.certSha256FingerPrint = in.createByteArray();
+                    return certCredential;
+                }
+
+                @Override
+                public CertificateCredential[] newArray(int size) {
+                    return new CertificateCredential[size];
+                }
+            };
+    }
+    public CertificateCredential certCredential = null;
+
+    /**
+     * SIM (Subscriber Identify Module) based credential.
+     * Contains fields under PerProviderSubscription/Credential/SIM subtree.
+     */
+    public static final class SimCredential implements Parcelable {
+        /**
+         * International Mobile device Subscriber Identity.
+         */
+        public String imsi = null;
+
+        /**
+         * EAP (Extensible Authentication Protocol) method type for using SIM credential.
+         * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
+         * for valid values.
+         * Using Integer.MIN_VALUE to indicate unset value.
+         */
+        public int eapType = Integer.MIN_VALUE;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object thatObject) {
+            if (this == thatObject) {
+                return true;
+            }
+            if (!(thatObject instanceof SimCredential)) {
+                return false;
+            }
+
+            SimCredential that = (SimCredential) thatObject;
+            return TextUtils.equals(imsi, that.imsi) &&
+                    eapType == that.eapType;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(imsi);
+            dest.writeInt(eapType);
+        }
+
+        public static final Creator<SimCredential> CREATOR =
+            new Creator<SimCredential>() {
+                @Override
+                public SimCredential createFromParcel(Parcel in) {
+                    SimCredential simCredential = new SimCredential();
+                    simCredential.imsi = in.readString();
+                    simCredential.eapType = in.readInt();
+                    return simCredential;
+                }
+
+                @Override
+                public SimCredential[] newArray(int size) {
+                    return new SimCredential[size];
+                }
+            };
+    }
+    public SimCredential simCredential = null;
+
+    /**
+     * CA (Certificate Authority) X509 certificate.
+     */
+    public X509Certificate caCertificate = null;
+
+    /**
+     * Client side X509 certificate chain.
+     */
+    public X509Certificate[] clientCertificateChain = null;
+
+    /**
+     * Client side private key.
+     */
+    public PrivateKey clientPrivateKey = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(realm);
+        dest.writeParcelable(userCredential, flags);
+        dest.writeParcelable(certCredential, flags);
+        dest.writeParcelable(simCredential, flags);
+        ParcelUtil.writeCertificate(dest, caCertificate);
+        ParcelUtil.writeCertificates(dest, clientCertificateChain);
+        ParcelUtil.writePrivateKey(dest, clientPrivateKey);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof Credential)) {
+            return false;
+        }
+
+        Credential that = (Credential) thatObject;
+        return TextUtils.equals(realm, that.realm) &&
+                (userCredential == null ? that.userCredential == null :
+                    userCredential.equals(that.userCredential)) &&
+                (certCredential == null ? that.certCredential == null :
+                    certCredential.equals(that.certCredential)) &&
+                (simCredential == null ? that.simCredential == null :
+                    simCredential.equals(that.simCredential)) &&
+                isX509CertificateEquals(caCertificate, that.caCertificate) &&
+                isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain) &&
+                isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
+    }
+
+    public static final Creator<Credential> CREATOR =
+        new Creator<Credential>() {
+            @Override
+            public Credential createFromParcel(Parcel in) {
+                Credential credential = new Credential();
+                credential.realm = in.readString();
+                credential.userCredential = in.readParcelable(null);
+                credential.certCredential = in.readParcelable(null);
+                credential.simCredential = in.readParcelable(null);
+                credential.caCertificate = ParcelUtil.readCertificate(in);
+                credential.clientCertificateChain = ParcelUtil.readCertificates(in);
+                credential.clientPrivateKey = ParcelUtil.readPrivateKey(in);
+                return credential;
+            }
+
+            @Override
+            public Credential[] newArray(int size) {
+                return new Credential[size];
+            }
+        };
+
+    private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) {
+        if (key1 == null && key2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (key1 == null || key2 == null) {
+            return false;
+        }
+
+        return TextUtils.equals(key1.getAlgorithm(), key2.getAlgorithm()) &&
+                Arrays.equals(key1.getEncoded(), key2.getEncoded());
+    }
+
+    private static boolean isX509CertificateEquals(X509Certificate cert1, X509Certificate cert2) {
+        if (cert1 == null && cert2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (cert1 == null || cert2 == null) {
+            return false;
+        }
+
+        boolean result = false;
+        try {
+            result = Arrays.equals(cert1.getEncoded(), cert2.getEncoded());
+        } catch (CertificateEncodingException e) {
+            /* empty, return false. */
+        }
+        return result;
+    }
+
+    private static boolean isX509CertificatesEquals(X509Certificate[] certs1,
+                                                    X509Certificate[] certs2) {
+        if (certs1 == null && certs2 == null) {
+            return true;
+        }
+
+        /* Return false if only one of them is null */
+        if (certs1 == null || certs2 == null) {
+            return false;
+        }
+
+        if (certs1.length != certs2.length) {
+            return false;
+        }
+
+        for (int i = 0; i < certs1.length; i++) {
+            if (!isX509CertificateEquals(certs1[i], certs2[i])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
new file mode 100644
index 0000000..62d5603
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2.pps;
+
+parcelable HomeSP;
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
new file mode 100644
index 0000000..2acc8be
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2016, 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.wifi.hotspot2.pps;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+
+/**
+ * Class representing HomeSP subtree in PerProviderSubscription (PPS)
+ * Management Object (MO) tree.
+ *
+ * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
+ * Release 2 Technical Specification.
+ *
+ * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
+ *
+ * @hide
+ */
+public final class HomeSP implements Parcelable {
+    /**
+     * FQDN (Fully Qualified Domain Name) of this home service provider.
+     */
+    public String fqdn = null;
+
+    /**
+     * Friendly name of this home service provider.
+     */
+    public String friendlyName = null;
+
+    /**
+     * List of Organization Identifiers (OIs) identifying a roaming consortium of
+     * which this provider is a member.
+     */
+    public long[] roamingConsortiumOIs = null;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(fqdn);
+        dest.writeString(friendlyName);
+        dest.writeLongArray(roamingConsortiumOIs);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof HomeSP)) {
+            return false;
+        }
+        HomeSP that = (HomeSP) thatObject;
+
+        return TextUtils.equals(fqdn, that.fqdn) &&
+                TextUtils.equals(friendlyName, that.friendlyName) &&
+                Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
+    }
+
+    public static final Creator<HomeSP> CREATOR =
+        new Creator<HomeSP>() {
+            @Override
+            public HomeSP createFromParcel(Parcel in) {
+                HomeSP homeSp = new HomeSP();
+                homeSp.fqdn = in.readString();
+                homeSp.friendlyName = in.readString();
+                homeSp.roamingConsortiumOIs = in.createLongArray();
+                return homeSp;
+            }
+
+            @Override
+            public HomeSP[] newArray(int size) {
+                return new HomeSP[size];
+            }
+        };
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
new file mode 100644
index 0000000..be11f0e
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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.wifi.hotspot2;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
+ */
+@SmallTest
+public class PasspointConfigurationTest {
+
+    private static HomeSP createHomeSp() {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        return homeSp;
+    }
+
+    private static Credential createCredential() {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = null;
+        cred.certCredential = null;
+        cred.simCredential = null;
+        cred.caCertificate = null;
+        cred.clientCertificateChain = null;
+        cred.clientPrivateKey = null;
+        return cred;
+    }
+
+    private static void verifyParcel(PasspointConfiguration writeConfig) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeConfig.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        PasspointConfiguration readConfig =
+                PasspointConfiguration.CREATOR.createFromParcel(parcel);
+        assertTrue(readConfig.equals(writeConfig));
+    }
+
+    @Test
+    public void verifyParcelWithDefault() throws Exception {
+        verifyParcel(new PasspointConfiguration());
+    }
+
+    @Test
+    public void verifyParcelWithHomeSPAndCredential() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        config.credential = createCredential();
+        verifyParcel(config);
+    }
+
+    @Test
+    public void verifyParcelWithHomeSPOnly() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.homeSp = createHomeSp();
+        verifyParcel(config);
+    }
+
+    @Test
+    public void verifyParcelWithCredentialOnly() throws Exception {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.credential = createCredential();
+        verifyParcel(config);
+    }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
new file mode 100644
index 0000000..c2dcec6
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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.wifi.hotspot2.omadm;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.hotspot2.omadm.XMLNode;
+import android.net.wifi.hotspot2.omadm.XMLParser;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.omadm.XMLParser}.
+ */
+@SmallTest
+public class XMLParserTest {
+    XMLParser mParser;
+
+    private static XMLNode createNode(XMLNode parent, String tag, String text) {
+        XMLNode node = new XMLNode(parent, tag);
+        node.addText(text);
+        if (parent != null)
+            parent.addChild(node);
+        node.close();
+        return node;
+    }
+
+    /**
+     * Setup before tests.
+     */
+    @Before
+    public void setUp() throws Exception {
+        mParser = new XMLParser();
+    }
+
+    @Test(expected = IOException.class)
+    public void parseNullXML() throws Exception {
+        mParser.parse(null);
+    }
+
+    @Test(expected = IOException.class)
+    public void parseEmptyXML() throws Exception {
+        mParser.parse(new String());
+    }
+
+    @Test(expected = SAXException.class)
+    public void parseMalformedXML() throws Exception {
+        String malformedXmlTree = "<root><child1>test1</child2></root>";
+        mParser.parse(malformedXmlTree);
+    }
+
+    @Test
+    public void parseValidXMLTree() throws Exception {
+        String xmlTree = "<root><child1>test1</child1><child2>test2</child2></root>";
+
+        // Construct the expected XML tree.
+        XMLNode expectedRoot = createNode(null, "root", "");
+        createNode(expectedRoot, "child1", "test1");
+        createNode(expectedRoot, "child2", "test2");
+
+        XMLNode actualRoot = mParser.parse(xmlTree);
+        assertTrue(actualRoot.equals(expectedRoot));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
new file mode 100644
index 0000000..68ac4ef
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 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.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.FakeKeys;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.CredentialTest}.
+ */
+@SmallTest
+public class CredentialTest {
+    private static Credential createCredential(Credential.UserCredential userCred,
+                                               Credential.CertificateCredential certCred,
+                                               Credential.SimCredential simCred,
+                                               X509Certificate caCert,
+                                               X509Certificate[] clientCertificateChain,
+                                               PrivateKey clientPrivateKey) {
+        Credential cred = new Credential();
+        cred.realm = "realm";
+        cred.userCredential = userCred;
+        cred.certCredential = certCred;
+        cred.simCredential = simCred;
+        cred.caCertificate = caCert;
+        cred.clientCertificateChain = clientCertificateChain;
+        cred.clientPrivateKey = clientPrivateKey;
+        return cred;
+    }
+
+    private static Credential createCredentialWithCertificateCredential() {
+        Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+        certCred.certType = "x509v3";
+        certCred.certSha256FingerPrint = new byte[256];
+        return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
+                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+    }
+
+    private static Credential createCredentialWithSimCredential() {
+        Credential.SimCredential simCred = new Credential.SimCredential();
+        simCred.imsi = "imsi";
+        simCred.eapType = 1;
+        return createCredential(null, null, simCred, null, null, null);
+    }
+
+    private static Credential createCredentialWithUserCredential() {
+        Credential.UserCredential userCred = new Credential.UserCredential();
+        userCred.username = "username";
+        userCred.password = "password";
+        userCred.eapType = 1;
+        userCred.nonEapInnerMethod = "MS-CHAP";
+        return createCredential(userCred, null, null, FakeKeys.CA_CERT0,
+                new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
+    }
+
+    private static void verifyParcel(Credential writeCred) {
+        Parcel parcel = Parcel.obtain();
+        writeCred.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        Credential readCred = Credential.CREATOR.createFromParcel(parcel);
+        assertTrue(readCred.equals(writeCred));
+    }
+
+    @Test
+    public void verifyParcelWithDefault() throws Exception {
+        verifyParcel(new Credential());
+    }
+
+    @Test
+    public void verifyParcelWithCertificateCredential() throws Exception {
+        verifyParcel(createCredentialWithCertificateCredential());
+    }
+
+    @Test
+    public void verifyParcelWithSimCredential() throws Exception {
+        verifyParcel(createCredentialWithSimCredential());
+    }
+
+    @Test
+    public void verifyParcelWithUserCredential() throws Exception {
+        verifyParcel(createCredentialWithUserCredential());
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
new file mode 100644
index 0000000..0d2da64
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.wifi.hotspot2.pps;
+
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSP}.
+ */
+@SmallTest
+public class HomeSPTest {
+    private static HomeSP createHomeSp() {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        return homeSp;
+    }
+
+    private static void verifyParcel(HomeSP writeHomeSp) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeHomeSp.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        HomeSP readHomeSp = HomeSP.CREATOR.createFromParcel(parcel);
+        assertTrue(readHomeSp.equals(writeHomeSp));
+    }
+
+    @Test
+    public void verifyParcelWithEmptyHomeSP() throws Exception {
+        verifyParcel(new HomeSP());
+    }
+
+    @Test
+    public void verifyParcelWithValidHomeSP() throws Exception {
+        verifyParcel(createHomeSp());
+    }
+}