/*
 * 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 android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_ENTRIES;
import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_OI;
import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_STRING_LENGTH;

import static junit.framework.Assert.assertNull;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

import android.net.wifi.EAPConstants;
import android.net.wifi.FakeKeys;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSp;
import android.os.Parcel;

import androidx.test.filters.SmallTest;

import com.android.modules.utils.build.SdkLevel;

import com.google.common.base.Strings;

import org.junit.Test;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}.
 */
@SmallTest
public class PasspointConfigurationTest {
    private static final int MAX_URL_BYTES = 2048;
    private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
    private static final String TEST_DECORATED_IDENTITY_PREFIX = "androidwifi.dev!";
    private static final long SUBSCRIPTION_EXPIRATION_TIME_MS = 1643996661000L;
    /**
     * Verify parcel write and read consistency for the given configuration.
     *
     * @param writeConfig The configuration to verify
     * @throws Exception
     */
    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));
    }

    /**
     * Verify parcel read/write for a default configuration.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithDefault() throws Exception {
        verifyParcel(new PasspointConfiguration());
    }

    /**
     * Verify parcel read/write for a configuration that contained the full configuration.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithFullConfiguration() throws Exception {
        verifyParcel(PasspointTestUtils.createConfig());
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain a list of service names.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutServiceNames() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setServiceFriendlyNames(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain HomeSP.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutHomeSP() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setHomeSp(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain Credential.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutCredential() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setCredential(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain Policy.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutPolicy() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setPolicy(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain subscription update.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutSubscriptionUpdate() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setSubscriptionUpdate(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain trust root certificate
     * list.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutTrustRootCertList() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setTrustRootCertList(null);
        verifyParcel(config);
    }

    /**
     * Verify parcel read/write for a configuration that doesn't contain AAA server trusted names
     * list.
     *
     * @throws Exception
     */
    @Test
    public void verifyParcelWithoutAaaServerTrustedNames() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setAaaServerTrustedNames(null);
        verifyParcel(config);
    }

    /**
     * Verify that a default/empty configuration is invalid.
     *
     * @throws Exception
     */
    @Test
    public void validateDefaultConfig() throws Exception {
        PasspointConfiguration config = new PasspointConfiguration();

        assertFalse(config.validate());
        assertFalse(config.validateForR2());
        assertTrue(config.isAutojoinEnabled());
        assertTrue(config.isMacRandomizationEnabled());
        assertFalse(config.isNonPersistentMacRandomizationEnabled());
        assertTrue(config.getMeteredOverride() == METERED_OVERRIDE_NONE);
    }

    /**
     * Verify that a configuration containing all fields is valid for R1/R2.
     *
     * @throws Exception
     */
    @Test
    public void validateFullConfig() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();

        assertTrue(config.validate());
        assertFalse(config.isOsuProvisioned());
    }

    /**
     * Verify that a configuration containing all fields except for UpdateIdentifier is valid for
     * R1, but invalid for R2.
     *
     * @throws Exception
     */
    @Test
    public void validateFullConfigWithoutUpdateIdentifier() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setUpdateIdentifier(Integer.MIN_VALUE);

        assertTrue(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that a configuration without Credential is invalid.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithoutCredential() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setCredential(null);

        assertFalse(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that a configuration without HomeSP is invalid.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithoutHomeSp() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setHomeSp(null);

        assertFalse(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that a configuration without Policy is valid, since Policy configurations
     * are optional for R1 and R2.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithoutPolicy() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setPolicy(null);

        assertTrue(config.validate());
    }

    /**
     * Verify that a configuration without subscription update is valid for R1 and invalid for R2,
     * since subscription update configuration is only applicable for R2.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithoutSubscriptionUpdate() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setSubscriptionUpdate(null);

        assertTrue(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that a configuration without AAA server trusted names is valid for R1 and R2,
     * since AAA server trusted names are optional for R1 and R2.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithoutAaaServerTrustedNames() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setAaaServerTrustedNames(null);

        assertTrue(config.validate());
    }

    /**
     * Verify that a configuration with a trust root certificate URL exceeding the max size
     * is invalid.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithInvalidTrustRootCertUrl() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
        Map<String, byte[]> trustRootCertList = new HashMap<>();
        Arrays.fill(rawUrlBytes, (byte) 'a');
        trustRootCertList.put(new String(rawUrlBytes, StandardCharsets.UTF_8),
                new byte[CERTIFICATE_FINGERPRINT_BYTES]);
        config.setTrustRootCertList(trustRootCertList);

        assertFalse(config.validate());

        trustRootCertList = new HashMap<>();
        trustRootCertList.put(null, new byte[CERTIFICATE_FINGERPRINT_BYTES]);
        config.setTrustRootCertList(trustRootCertList);

        assertFalse(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that a configuration with an invalid trust root certificate fingerprint is invalid.
     *
     * @throws Exception
     */
    @Test
    public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        Map<String, byte[]> trustRootCertList = new HashMap<>();
        trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]);
        config.setTrustRootCertList(trustRootCertList);
        assertFalse(config.validate());

        trustRootCertList = new HashMap<>();
        trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES - 1]);
        config.setTrustRootCertList(trustRootCertList);
        assertFalse(config.validate());

        trustRootCertList = new HashMap<>();
        trustRootCertList.put("test.cert.com", null);
        config.setTrustRootCertList(trustRootCertList);
        assertFalse(config.validate());
        assertFalse(config.validateForR2());
    }

    /**
     * Verify that copy constructor works when pass in a null source.
     *
     * @throws Exception
     */
    @Test
    public void validateCopyConstructorWithNullSource() throws Exception {
        PasspointConfiguration copyConfig = new PasspointConfiguration(null);
        PasspointConfiguration defaultConfig = new PasspointConfiguration();
        assertTrue(copyConfig.equals(defaultConfig));
    }

    /**
     * Verify that copy constructor works when pass in a valid source.
     *
     * @throws Exception
     */
    @Test
    public void validateCopyConstructorWithValidSource() throws Exception {
        PasspointConfiguration sourceConfig = PasspointTestUtils.createConfig();
        PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
        assertTrue(copyConfig.equals(sourceConfig));
    }

    /**
     * Verify that a configuration containing all fields is valid for R2.
     *
     * @throws Exception
     */
    @Test
    public void validateFullR2Config() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createR2Config();
        assertTrue(config.validate());
        assertTrue(config.validateForR2());
        assertTrue(config.isOsuProvisioned());
    }

    /**
     * Verify that the unique identifier generated is identical for two instances
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueId() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();

        assertEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is the same for two instances with different
     * HomeSp node but same FQDN
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdDifferentHomeSpSameFqdn() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();

        // Modify config2's RCOIs and friendly name to a different set of values
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        HomeSp homeSp = config2.getHomeSp();
        homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb});
        homeSp.setFriendlyName("Some other name");
        config2.setHomeSp(homeSp);

        assertEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is different for two instances with the same
     * HomeSp node but different FQDN
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdSameHomeSpDifferentFqdn() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();

        // Modify config2's FQDN to a different value
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        HomeSp homeSp = config2.getHomeSp();
        homeSp.setFqdn("fqdn2.com");
        config2.setHomeSp(homeSp);

        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is different for two instances with different
     * SIM Credential node
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdDifferentSimCredential() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();

        // Modify config2's realm and SIM credential to a different set of values
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        Credential credential = config2.getCredential();
        credential.setRealm("realm2.example.com");
        credential.getSimCredential().setImsi("350460*");
        config2.setCredential(credential);

        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is different for two instances with different
     * Realm in the Credential node
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdDifferentRealm() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();

        // Modify config2's realm to a different set of values
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        Credential credential = config2.getCredential();
        credential.setRealm("realm2.example.com");
        config2.setCredential(credential);

        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is the same for two instances with different
     * password and same username in the User Credential node
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdSameUserInUserCredential() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
        Credential credential = createCredentialWithUserCredential("user", "passwd");
        config1.setCredential(credential);

        // Modify config2's Passpowrd to a different set of values
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        credential = createCredentialWithUserCredential("user", "newpasswd");
        config2.setCredential(credential);

        assertEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is different for two instances with different
     * username in the User Credential node
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdDifferentUserCredential() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
        Credential credential = createCredentialWithUserCredential("user", "passwd");
        config1.setCredential(credential);

        // Modify config2's username to a different value
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        credential = createCredentialWithUserCredential("user2", "passwd");
        config2.setCredential(credential);

        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Verify that the unique identifier generated is different for two instances with different
     * Cert Credential node
     *
     * @throws Exception
     */
    @Test
    public void validateUniqueIdDifferentCertCredential() throws Exception {
        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
        Credential credential = createCredentialWithCertificateCredential(true, true);
        config1.setCredential(credential);

        // Modify config2's cert credential to a different set of values
        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
        credential = createCredentialWithCertificateCredential(false, false);
        config2.setCredential(credential);

        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
    }

    /**
     * Helper function for generating certificate credential for testing.
     *
     * @return {@link Credential}
     */
    private static Credential createCredentialWithCertificateCredential(Boolean useCaCert0,
            Boolean useCert0)
            throws NoSuchAlgorithmException, CertificateEncodingException {
        Credential.CertificateCredential certCred = new Credential.CertificateCredential();
        certCred.setCertType("x509v3");
        if (useCert0) {
            certCred.setCertSha256Fingerprint(
                    MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
        } else {
            certCred.setCertSha256Fingerprint(MessageDigest.getInstance("SHA-256")
                    .digest(FakeKeys.CLIENT_SUITE_B_RSA3072_CERT.getEncoded()));
        }
        return createCredential(null, certCred, null, new X509Certificate[] {FakeKeys.CLIENT_CERT},
                FakeKeys.RSA_KEY1, useCaCert0 ? FakeKeys.CA_CERT0 : FakeKeys.CA_CERT1);
    }

    /**
     * Helper function for generating user credential for testing.
     *
     * @return {@link Credential}
     */
    private static Credential createCredentialWithUserCredential(String username, String password) {
        Credential.UserCredential userCred = new Credential.UserCredential();
        userCred.setUsername(username);
        userCred.setPassword(password);
        userCred.setMachineManaged(true);
        userCred.setAbleToShare(true);
        userCred.setSoftTokenApp("TestApp");
        userCred.setEapType(EAPConstants.EAP_TTLS);
        userCred.setNonEapInnerMethod("MS-CHAP");
        return createCredential(userCred, null, null, null, null, FakeKeys.CA_CERT0);
    }

    /**
     * Helper function for generating Credential for testing.
     *
     * @param userCred Instance of UserCredential
     * @param certCred Instance of CertificateCredential
     * @param simCred Instance of SimCredential
     * @param clientCertificateChain Chain of client certificates
     * @param clientPrivateKey Client private key
     * @param caCerts CA certificates
     * @return {@link Credential}
     */
    private static Credential createCredential(Credential.UserCredential userCred,
            Credential.CertificateCredential certCred,
            Credential.SimCredential simCred,
            X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey,
            X509Certificate... caCerts) {
        Credential cred = new Credential();
        cred.setCreationTimeInMillis(123455L);
        cred.setExpirationTimeInMillis(2310093L);
        cred.setRealm("realm");
        cred.setCheckAaaServerCertStatus(true);
        cred.setUserCredential(userCred);
        cred.setCertCredential(certCred);
        cred.setSimCredential(simCred);
        if (caCerts != null && caCerts.length == 1) {
            cred.setCaCertificate(caCerts[0]);
        } else {
            cred.setCaCertificates(caCerts);
        }
        cred.setClientCertificateChain(clientCertificateChain);
        cred.setClientPrivateKey(clientPrivateKey);
        return cred;
    }

    /**
     * Verify that the unique identifier API generates an exception if HomeSP is not initialized.
     *
     * @throws Exception
     */
    @Test (expected = IllegalStateException.class)
    public void validateUniqueIdExceptionWithEmptyHomeSp() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setHomeSp(null);
        String uniqueId = config.getUniqueId();
    }

    /**
     * Verify that the unique identifier API generates an exception if Credential is not
     * initialized.
     *
     * @throws Exception
     */
    @Test (expected = IllegalStateException.class)
    public void validateUniqueIdExceptionWithEmptyCredential() throws Exception {
        PasspointConfiguration config = PasspointTestUtils.createConfig();
        config.setCredential(null);
        String uniqueId = config.getUniqueId();
    }

    /**
     * Verify that the set and get decorated identity prefix methods work as expected.
     *
     * @throws Exception
     */
    @Test
    public void testSetGetDecoratedIdentityPrefix() {
        assumeTrue(SdkLevel.isAtLeastS());
        PasspointConfiguration config = new PasspointConfiguration();

        assertNull(config.getDecoratedIdentityPrefix());
        config.setDecoratedIdentityPrefix(TEST_DECORATED_IDENTITY_PREFIX);
        assertEquals(TEST_DECORATED_IDENTITY_PREFIX, config.getDecoratedIdentityPrefix());
    }

    /**
     * Verify that the set decorated identity prefix doesn't accept a malformed input.
     *
     * @throws Exception
     */
    @Test (expected = IllegalArgumentException.class)
    public void testSetDecoratedIdentityPrefixWithInvalidValue() {
        assumeTrue(SdkLevel.isAtLeastS());
        PasspointConfiguration config = new PasspointConfiguration();

        config.setDecoratedIdentityPrefix(TEST_DECORATED_IDENTITY_PREFIX.replace('!', 'a'));
    }

    /**
     * Tests {@link PasspointConfiguration#getSubscriptionExpirationTimeMillis()} and
     * {@link PasspointConfiguration#setSubscriptionExpirationTimeInMillis(long)}
     * <p>
     */
    @Test
    public void testGetSetSubscriptionExpirationTimeMillis() throws Exception {
        PasspointConfiguration passpointConfiguration = new PasspointConfiguration();
        assertEquals(Long.MIN_VALUE,
                passpointConfiguration.getSubscriptionExpirationTimeMillis());
        passpointConfiguration
                .setSubscriptionExpirationTimeInMillis(SUBSCRIPTION_EXPIRATION_TIME_MS);
        assertEquals(SUBSCRIPTION_EXPIRATION_TIME_MS,
                passpointConfiguration.getSubscriptionExpirationTimeMillis());
    }

    @Test
    public void testValidatePasspointConfig() {
        PasspointConfiguration passpointConfiguration = new PasspointConfiguration();
        HomeSp homeSp = new HomeSp();
        homeSp.setFqdn(Strings.repeat("A", MAX_STRING_LENGTH));
        homeSp.setFriendlyName(Strings.repeat("A", MAX_STRING_LENGTH));
        List<String> homePartnersList = new ArrayList<>();
        for (int h = 0; h < MAX_NUMBER_OF_ENTRIES; h++) {
            homePartnersList.add(Strings.repeat("A", MAX_STRING_LENGTH));
        }
        homeSp.setOtherHomePartnersList(homePartnersList);
        homeSp.setIconUrl(Strings.repeat("A", MAX_URL_BYTES));

        Credential credential = new Credential();
        credential.setRealm("example.com");
        Credential.UserCredential userCredential = new Credential.UserCredential();
        userCredential.setUsername("username");
        userCredential.setPassword("password");
        userCredential.setEapType(21);
        userCredential.setNonEapInnerMethod("MS-CHAP");
        credential.setUserCredential(userCredential);
        passpointConfiguration.setCredential(credential);
        passpointConfiguration.setHomeSp(homeSp);
        assertTrue(passpointConfiguration.validate());

        // FQDN exceed the limit
        homeSp.setFqdn(Strings.repeat("A", MAX_STRING_LENGTH + 1));
        passpointConfiguration.setHomeSp(homeSp);
        assertFalse(passpointConfiguration.validate());
        homeSp.setFqdn(Strings.repeat("A", MAX_STRING_LENGTH));
        // Friendly name exceed the limit
        homeSp.setFriendlyName(Strings.repeat("A", MAX_STRING_LENGTH + 1));
        passpointConfiguration.setHomeSp(homeSp);
        assertFalse(passpointConfiguration.validate());
        homeSp.setFriendlyName(Strings.repeat("A", MAX_STRING_LENGTH));
        // Icon url exceed the limit
        homeSp.setIconUrl(Strings.repeat("A", MAX_URL_BYTES + 1));
        passpointConfiguration.setHomeSp(homeSp);
        assertFalse(passpointConfiguration.validate());
        homeSp.setIconUrl(Strings.repeat("A", MAX_URL_BYTES));
        // AAA service trusted names exceed the limit
        String[] aaaServerTrustedNames = new String[MAX_NUMBER_OF_ENTRIES + 1];
        passpointConfiguration.setAaaServerTrustedNames(aaaServerTrustedNames);
        passpointConfiguration.setHomeSp(homeSp);
        assertFalse(passpointConfiguration.validate());
        passpointConfiguration.setAaaServerTrustedNames(null);
        // Match all OIs exceed the limit
        long[] ois = new long[MAX_NUMBER_OF_OI + 1];
        homeSp.setMatchAllOis(ois);
        assertFalse(homeSp.validate());
        homeSp.setMatchAllOis(null);
        // Match any OIs exceed the limit
        homeSp.setMatchAnyOis(ois);
        assertFalse(homeSp.validate());
        homeSp.setMatchAnyOis(null);
        // Roaming Consortium OIs exceed the limit
        homeSp.setRoamingConsortiumOis(ois);
        assertFalse(homeSp.validate());
        homeSp.setRoamingConsortiumOis(null);
        // ServiceFriendlyNames exceed the limit
        HashMap<String, String> friendlyNames = new HashMap<>();
        for (int i = 0; i < MAX_NUMBER_OF_ENTRIES + 1; i++) {
            friendlyNames.put(String.valueOf(i), String.valueOf(i));
        }
        passpointConfiguration.setServiceFriendlyNames(friendlyNames);
        assertFalse(passpointConfiguration.validate());
    }
}
