From e557c35d95a8d05c58a592d4c34db69a1dd2c1a7 Mon Sep 17 00:00:00 2001 From: Peter Qiu Date: Thu, 29 Sep 2016 10:44:15 -0700 Subject: wifi: hotspot2: initial implementation of PasspointConfiguration Only support nodes under HomeSP and Credential subtree that are used by Hotspot 2.0 Release 1. These classes are used for data exchanges between the app and the system service (WifiService). All the business logics and validations will be done on the service side (WifiService). Bug: 31813706 Test: frameworks/base/wifi/tests/runtests.sh Change-Id: I6b08f52dd7b1a7b337899ab80f11d062c8e2f532 --- .../net/wifi/hotspot2/PasspointConfiguration.aidl | 19 ++ .../net/wifi/hotspot2/PasspointConfiguration.java | 78 +++++ .../android/net/wifi/hotspot2/pps/Credential.aidl | 19 ++ .../android/net/wifi/hotspot2/pps/Credential.java | 376 +++++++++++++++++++++ .../java/android/net/wifi/hotspot2/pps/HomeSP.aidl | 19 ++ .../java/android/net/wifi/hotspot2/pps/HomeSP.java | 96 ++++++ .../wifi/hotspot2/PasspointConfigurationTest.java | 90 +++++ .../net/wifi/hotspot2/pps/CredentialTest.java | 105 ++++++ .../android/net/wifi/hotspot2/pps/HomeSPTest.java | 59 ++++ 9 files changed, 861 insertions(+) create mode 100644 wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.aidl create mode 100644 wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java create mode 100644 wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl create mode 100644 wifi/java/android/net/wifi/hotspot2/pps/Credential.java create mode 100644 wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl create mode 100644 wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java create mode 100644 wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java create mode 100644 wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java create mode 100644 wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java 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 000000000000..6b1cea8edb5a --- /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 000000000000..18aae534d098 --- /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 CREATOR = + new Creator() { + @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/pps/Credential.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Credential.aidl new file mode 100644 index 000000000000..3d8e8330c2c4 --- /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 000000000000..92dbd8afb2d3 --- /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 CREATOR = + new Creator() { + @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 CREATOR = + new Creator() { + @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 CREATOR = + new Creator() { + @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 CREATOR = + new Creator() { + @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 000000000000..62d5603b5982 --- /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 000000000000..2acc8bec8007 --- /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 CREATOR = + new Creator() { + @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 000000000000..be11f0ee64c0 --- /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/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java new file mode 100644 index 000000000000..68ac4efa214f --- /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 000000000000..0d2da6404e7f --- /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()); + } +} -- cgit v1.2.3-59-g8ed1b From bae4becf6997843cddf0d0c1dfa1f63e4580e6f8 Mon Sep 17 00:00:00 2001 From: Peter Qiu Date: Fri, 14 Oct 2016 14:26:46 -0700 Subject: wifi: hotspot2: omadm: add XML parser for parsing OMA-DM XML string The XML parser will parse an XML string into a tree-like structure represented by XMLNode, which will be used by an object specific parser (e.g. PerProviderSubscription Management Object tree parser) for further processing. Bug: 32129686 Test: frameworks/base/wifi/tests/runtests.sh Change-Id: I69c1931b2e8a6f3551e4eba5cffa3d2d0877872a --- .../android/net/wifi/hotspot2/omadm/XMLNode.java | 103 ++++++++++++++++++++ .../android/net/wifi/hotspot2/omadm/XMLParser.java | 108 +++++++++++++++++++++ .../net/wifi/hotspot2/omadm/XMLParserTest.java | 83 ++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java create mode 100644 wifi/java/android/net/wifi/hotspot2/omadm/XMLParser.java create mode 100644 wifi/tests/src/android/net/wifi/hotspot2/omadm/XMLParserTest.java 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 000000000000..e87698cb7ed1 --- /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 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 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 000000000000..948052c9bf8a --- /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: + * + * text1 + * + * text3 + * + * + * + * 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/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 000000000000..c2dcec693b83 --- /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 = "test1"; + mParser.parse(malformedXmlTree); + } + + @Test + public void parseValidXMLTree() throws Exception { + String xmlTree = "test1test2"; + + // 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)); + } +} -- cgit v1.2.3-59-g8ed1b