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());
+ }
+}