/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi.hotspot2; import android.annotation.Nullable; import android.net.wifi.hotspot2.PasspointConfiguration; import android.text.TextUtils; import com.android.internal.util.XmlUtils; import com.android.server.wifi.SIMAccessor; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiKeyStore; import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Responsible for Passpoint specific configuration store data. There are two types of * configuration data, system wide and user specific. The system wide configurations are stored * in the share store and user specific configurations are store in the user store. * * Below are the current configuration data for each respective store file, the list will * probably grow in the future. * * Share Store (system wide configurations) * - Current provider index - use for assigning provider ID during provider creation, to make * sure each provider will have an unique ID across all users. * * User Store (user specific configurations) * - Provider list - list of Passpoint provider configurations * */ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA = "PasspointConfigData"; private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST = "ProviderList"; private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER = "Provider"; private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION = "Configuration"; private static final String XML_TAG_PROVIDER_ID = "ProviderID"; private static final String XML_TAG_CREATOR_UID = "CreatorUID"; private static final String XML_TAG_PACKAGE_NAME = "PackageName"; private static final String XML_TAG_CA_CERTIFICATE_ALIASES = "CaCertificateAliases"; private static final String XML_TAG_CA_CERTIFICATE_ALIAS = "CaCertificateAlias"; private static final String XML_TAG_CLIENT_CERTIFICATE_ALIAS = "ClientCertificateAlias"; private static final String XML_TAG_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKeyAlias"; private static final String XML_TAG_REMEDIATION_CA_CERTIFICATE_ALIAS = "RemediationCaCertificateAlias"; private static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected"; private final WifiKeyStore mKeyStore; private final SIMAccessor mSimAccessor; private final DataSource mDataSource; /** * Interface define the data source for the Passpoint configuration store data. */ public interface DataSource { /** * Retrieve the provider list from the data source. * * @return List of {@link PasspointProvider} */ List getProviders(); /** * Set the provider list in the data source. * * @param providers The list of providers */ void setProviders(List providers); } PasspointConfigUserStoreData(WifiKeyStore keyStore, SIMAccessor simAccessor, DataSource dataSource) { mKeyStore = keyStore; mSimAccessor = simAccessor; mDataSource = dataSource; } @Override public void serializeData(XmlSerializer out, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeUserData(out); } @Override public void deserializeData(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { return; } deserializeUserData(in, outerTagDepth); } /** * Reset user data (user specific Passpoint configurations). */ @Override public void resetData() { mDataSource.setProviders(new ArrayList()); } @Override public boolean hasNewDataToSerialize() { // always persist. return true; } @Override public String getName() { return XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA; } @Override public @WifiConfigStore.StoreFileId int getStoreFileId() { // Shared general store. return WifiConfigStore.STORE_FILE_USER_GENERAL; } /** * Serialize user data (user specific Passpoint configurations) to a XML block. * * @param out The output stream to serialize data to * @throws XmlPullParserException * @throws IOException */ private void serializeUserData(XmlSerializer out) throws XmlPullParserException, IOException { serializeProviderList(out, mDataSource.getProviders()); } /** * Serialize the list of Passpoint providers from the data source to a XML block. * * @param out The output stream to serialize data to * @param providerList The list of providers to serialize * @throws XmlPullParserException * @throws IOException */ private void serializeProviderList(XmlSerializer out, List providerList) throws XmlPullParserException, IOException { if (providerList == null) { return; } XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST); for (PasspointProvider provider : providerList) { if (provider.isEphemeral()) { continue; } serializeProvider(out, provider); } XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST); } /** * Serialize a Passpoint provider to a XML block. * * @param out The output stream to serialize data to * @param provider The provider to serialize * @throws XmlPullParserException * @throws IOException */ private void serializeProvider(XmlSerializer out, PasspointProvider provider) throws XmlPullParserException, IOException { XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER); XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_ID, provider.getProviderId()); XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, provider.getCreatorUid()); if (provider.getPackageName() != null) { XmlUtil.writeNextValue(out, XML_TAG_PACKAGE_NAME, provider.getPackageName()); } XmlUtil.writeNextValue(out, XML_TAG_CA_CERTIFICATE_ALIASES, provider.getCaCertificateAliases()); XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERTIFICATE_ALIAS, provider.getClientCertificateAlias()); XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS, provider.getClientPrivateKeyAlias()); XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_CONNECTED, provider.getHasEverConnected()); if (provider.getConfig() != null) { XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig()); XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION); } XmlUtil.writeNextValue(out, XML_TAG_REMEDIATION_CA_CERTIFICATE_ALIAS, provider.getRemediationCaCertificateAlias()); XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER); } /** * Deserialize user data (user specific Passpoint configurations) from the input stream. * * @param in The input stream to read data from * @param outerTagDepth The tag depth of the current XML section * @throws XmlPullParserException * @throws IOException */ private void deserializeUserData(XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException { String[] headerName = new String[1]; while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) { switch (headerName[0]) { case XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST: mDataSource.setProviders(deserializeProviderList(in, outerTagDepth + 1)); break; default: throw new XmlPullParserException("Unknown Passpoint user store data " + headerName[0]); } } } /** * Deserialize a list of Passpoint providers from the input stream. * * @param in The input stream to read data form * @param outerTagDepth The tag depth of the current XML section * @return List of {@link PasspointProvider} * @throws XmlPullParserException * @throws IOException */ private List deserializeProviderList(XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException { List providerList = new ArrayList<>(); while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER, outerTagDepth)) { providerList.add(deserializeProvider(in, outerTagDepth + 1)); } return providerList; } /** * Deserialize a Passpoint provider from the input stream. * * @param in The input stream to read data from * @param outerTagDepth The tag depth of the current XML section * @return {@link PasspointProvider} * @throws XmlPullParserException * @throws IOException */ private PasspointProvider deserializeProvider(XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException { long providerId = Long.MIN_VALUE; int creatorUid = Integer.MIN_VALUE; List caCertificateAliases = null; String caCertificateAlias = null; String clientCertificateAlias = null; String clientPrivateKeyAlias = null; String remediationCaCertificateAlias = null; String packageName = null; boolean hasEverConnected = false; boolean shared = false; PasspointConfiguration config = null; while (XmlUtils.nextElementWithin(in, outerTagDepth)) { if (in.getAttributeValue(null, "name") != null) { // Value elements. String[] name = new String[1]; Object value = XmlUtil.readCurrentValue(in, name); switch (name[0]) { case XML_TAG_PROVIDER_ID: providerId = (long) value; break; case XML_TAG_CREATOR_UID: creatorUid = (int) value; break; case XML_TAG_PACKAGE_NAME: packageName = (String) value; break; case XML_TAG_CA_CERTIFICATE_ALIASES: caCertificateAliases = (List) value; break; case XML_TAG_CA_CERTIFICATE_ALIAS: // Backwards compatibility: for the case that installs a profile that // uses this alias. caCertificateAlias = (String) value; break; case XML_TAG_CLIENT_CERTIFICATE_ALIAS: clientCertificateAlias = (String) value; break; case XML_TAG_CLIENT_PRIVATE_KEY_ALIAS: clientPrivateKeyAlias = (String) value; break; case XML_TAG_REMEDIATION_CA_CERTIFICATE_ALIAS: remediationCaCertificateAlias = (String) value; break; case XML_TAG_HAS_EVER_CONNECTED: hasEverConnected = (boolean) value; break; } } else { if (!TextUtils.equals(in.getName(), XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION)) { throw new XmlPullParserException("Unexpected section under Provider: " + in.getName()); } config = PasspointXmlUtils.deserializePasspointConfiguration(in, outerTagDepth + 1); } } if (providerId == Long.MIN_VALUE) { throw new XmlPullParserException("Missing provider ID"); } if (caCertificateAliases != null && caCertificateAlias != null) { throw new XmlPullParserException( "Should not have valid entry for caCertificateAliases and caCertificateAlias " + "at the same time"); } if (caCertificateAlias != null) { caCertificateAliases = Arrays.asList(caCertificateAlias); } if (config == null) { throw new XmlPullParserException("Missing Passpoint configuration"); } return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid, packageName, caCertificateAliases, clientCertificateAlias, clientPrivateKeyAlias, remediationCaCertificateAlias, hasEverConnected, shared); } }