1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.hotspot2;
18
19import android.net.wifi.hotspot2.PasspointConfiguration;
20import android.text.TextUtils;
21
22import com.android.internal.util.XmlUtils;
23import com.android.server.wifi.SIMAccessor;
24import com.android.server.wifi.WifiConfigStore;
25import com.android.server.wifi.WifiKeyStore;
26import com.android.server.wifi.util.XmlUtil;
27
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.List;
35
36/**
37 * Responsible for Passpoint specific configuration store data.  There are two types of
38 * configuration data, system wide and user specific.  The system wide configurations are stored
39 * in the share store and user specific configurations are store in the user store.
40 *
41 * Below are the current configuration data for each respective store file, the list will
42 * probably grow in the future.
43 *
44 * Share Store (system wide configurations)
45 * - Current provider index - use for assigning provider ID during provider creation, to make
46 *                            sure each provider will have an unique ID across all users.
47 *
48 * User Store (user specific configurations)
49 * - Provider list - list of Passpoint provider configurations
50 *
51 */
52public class PasspointConfigStoreData implements WifiConfigStore.StoreData {
53    private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA =
54            "PasspointConfigData";
55    private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST =
56            "ProviderList";
57    private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER =
58            "Provider";
59    private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION =
60            "Configuration";
61
62    private static final String XML_TAG_PROVIDER_ID = "ProviderID";
63    private static final String XML_TAG_CREATOR_UID = "CreatorUID";
64    private static final String XML_TAG_CA_CERTIFICATE_ALIAS = "CaCertificateAlias";
65    private static final String XML_TAG_CLIENT_CERTIFICATE_ALIAS = "ClientCertificateAlias";
66    private static final String XML_TAG_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKeyAlias";
67
68    private static final String XML_TAG_PROVIDER_INDEX = "ProviderIndex";
69    private static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected";
70
71    private final WifiKeyStore mKeyStore;
72    private final SIMAccessor mSimAccessor;
73    private final DataSource mDataSource;
74
75    /**
76     * Interface define the data source for the Passpoint configuration store data.
77     */
78    public interface DataSource {
79        /**
80         * Retrieve the provider list from the data source.
81         *
82         * @return List of {@link PasspointProvider}
83         */
84        List<PasspointProvider> getProviders();
85
86        /**
87         * Set the provider list in the data source.
88         *
89         * @param providers The list of providers
90         */
91        void setProviders(List<PasspointProvider> providers);
92
93        /**
94         * Retrieve the current provider index.
95         *
96         * @return long
97         */
98        long getProviderIndex();
99
100        /**
101         * Set the current provider index.
102         *
103         * @param providerIndex The provider index used for provider creation
104         */
105        void setProviderIndex(long providerIndex);
106    }
107
108    PasspointConfigStoreData(WifiKeyStore keyStore, SIMAccessor simAccessor,
109            DataSource dataSource) {
110        mKeyStore = keyStore;
111        mSimAccessor = simAccessor;
112        mDataSource = dataSource;
113    }
114
115    @Override
116    public void serializeData(XmlSerializer out, boolean shared)
117            throws XmlPullParserException, IOException {
118        if (shared) {
119            serializeShareData(out);
120        } else {
121            serializeUserData(out);
122        }
123    }
124
125    @Override
126    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
127            throws XmlPullParserException, IOException {
128        // Ignore empty reads.
129        if (in == null) {
130            return;
131        }
132        if (shared) {
133            deserializeShareData(in, outerTagDepth);
134        } else {
135            deserializeUserData(in, outerTagDepth);
136        }
137    }
138
139    @Override
140    public void resetData(boolean shared) {
141        if (shared) {
142            resetShareData();
143        } else {
144            resetUserData();
145        }
146    }
147
148    @Override
149    public String getName() {
150        return XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA;
151    }
152
153    @Override
154    public boolean supportShareData() {
155        return true;
156    }
157
158    /**
159     * Serialize share data (system wide Passpoint configurations) to a XML block.
160     *
161     * @param out The output stream to serialize data to
162     * @throws XmlPullParserException
163     * @throws IOException
164     */
165    private void serializeShareData(XmlSerializer out) throws XmlPullParserException, IOException {
166        XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_INDEX, mDataSource.getProviderIndex());
167    }
168
169    /**
170     * Serialize user data (user specific Passpoint configurations) to a XML block.
171     *
172     * @param out The output stream to serialize data to
173     * @throws XmlPullParserException
174     * @throws IOException
175     */
176    private void serializeUserData(XmlSerializer out) throws XmlPullParserException, IOException {
177        serializeProviderList(out, mDataSource.getProviders());
178    }
179
180    /**
181     * Serialize the list of Passpoint providers from the data source to a XML block.
182     *
183     * @param out The output stream to serialize data to
184     * @param providerList The list of providers to serialize
185     * @throws XmlPullParserException
186     * @throws IOException
187     */
188    private void serializeProviderList(XmlSerializer out, List<PasspointProvider> providerList)
189            throws XmlPullParserException, IOException {
190        if (providerList == null) {
191            return;
192        }
193        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
194        for (PasspointProvider provider : providerList) {
195            serializeProvider(out, provider);
196        }
197        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
198    }
199
200    /**
201     * Serialize a Passpoint provider to a XML block.
202     *
203     * @param out The output stream to serialize data to
204     * @param provider The provider to serialize
205     * @throws XmlPullParserException
206     * @throws IOException
207     */
208    private void serializeProvider(XmlSerializer out, PasspointProvider provider)
209            throws XmlPullParserException, IOException {
210        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
211        XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_ID, provider.getProviderId());
212        XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, provider.getCreatorUid());
213        XmlUtil.writeNextValue(out, XML_TAG_CA_CERTIFICATE_ALIAS,
214                provider.getCaCertificateAlias());
215        XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERTIFICATE_ALIAS,
216                provider.getClientCertificateAlias());
217        XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS,
218                provider.getClientPrivateKeyAlias());
219        XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_CONNECTED, provider.getHasEverConnected());
220        if (provider.getConfig() != null) {
221            XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
222            PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig());
223            XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
224        }
225        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
226    }
227
228    /**
229     * Deserialize share data (system wide Passpoint configurations) from the input stream.
230     *
231     * @param in The input stream to read data from
232     * @param outerTagDepth The tag depth of the current XML section
233     * @throws XmlPullParserException
234     * @throws IOException
235     */
236    private void deserializeShareData(XmlPullParser in, int outerTagDepth)
237            throws XmlPullParserException, IOException {
238        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
239            String[] valueName = new String[1];
240            Object value = XmlUtil.readCurrentValue(in, valueName);
241            if (valueName[0] == null) {
242                throw new XmlPullParserException("Missing value name");
243            }
244            switch (valueName[0]) {
245                case XML_TAG_PROVIDER_INDEX:
246                    mDataSource.setProviderIndex((long) value);
247                    break;
248                default:
249                    throw new XmlPullParserException("Unknown value under share store data "
250                            + valueName[0]);
251            }
252        }
253    }
254
255    /**
256     * Deserialize user data (user specific Passpoint configurations) from the input stream.
257     *
258     * @param in The input stream to read data from
259     * @param outerTagDepth The tag depth of the current XML section
260     * @throws XmlPullParserException
261     * @throws IOException
262     */
263    private void deserializeUserData(XmlPullParser in, int outerTagDepth)
264            throws XmlPullParserException, IOException {
265        String[] headerName = new String[1];
266        while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
267            switch (headerName[0]) {
268                case XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST:
269                    mDataSource.setProviders(deserializeProviderList(in, outerTagDepth + 1));
270                    break;
271                default:
272                    throw new XmlPullParserException("Unknown Passpoint user store data "
273                            + headerName[0]);
274            }
275        }
276    }
277
278    /**
279     * Deserialize a list of Passpoint providers from the input stream.
280     *
281     * @param in The input stream to read data form
282     * @param outerTagDepth The tag depth of the current XML section
283     * @return List of {@link PasspointProvider}
284     * @throws XmlPullParserException
285     * @throws IOException
286     */
287    private List<PasspointProvider> deserializeProviderList(XmlPullParser in, int outerTagDepth)
288            throws XmlPullParserException, IOException {
289        List<PasspointProvider> providerList = new ArrayList<>();
290        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER,
291                outerTagDepth)) {
292            providerList.add(deserializeProvider(in, outerTagDepth + 1));
293        }
294        return providerList;
295    }
296
297    /**
298     * Deserialize a Passpoint provider from the input stream.
299     *
300     * @param in The input stream to read data from
301     * @param outerTagDepth The tag depth of the current XML section
302     * @return {@link PasspointProvider}
303     * @throws XmlPullParserException
304     * @throws IOException
305     */
306    private PasspointProvider deserializeProvider(XmlPullParser in, int outerTagDepth)
307            throws XmlPullParserException, IOException {
308        long providerId = Long.MIN_VALUE;
309        int creatorUid = Integer.MIN_VALUE;
310        String caCertificateAlias = null;
311        String clientCertificateAlias = null;
312        String clientPrivateKeyAlias = null;
313        boolean hasEverConnected = false;
314        boolean shared = false;
315        PasspointConfiguration config = null;
316        while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
317            if (in.getAttributeValue(null, "name") != null) {
318                // Value elements.
319                String[] name = new String[1];
320                Object value = XmlUtil.readCurrentValue(in, name);
321                switch (name[0]) {
322                    case XML_TAG_PROVIDER_ID:
323                        providerId = (long) value;
324                        break;
325                    case XML_TAG_CREATOR_UID:
326                        creatorUid = (int) value;
327                        break;
328                    case XML_TAG_CA_CERTIFICATE_ALIAS:
329                        caCertificateAlias = (String) value;
330                        break;
331                    case XML_TAG_CLIENT_CERTIFICATE_ALIAS:
332                        clientCertificateAlias = (String) value;
333                        break;
334                    case XML_TAG_CLIENT_PRIVATE_KEY_ALIAS:
335                        clientPrivateKeyAlias = (String) value;
336                        break;
337                    case XML_TAG_HAS_EVER_CONNECTED:
338                        hasEverConnected = (boolean) value;
339                        break;
340                }
341            } else {
342                if (!TextUtils.equals(in.getName(),
343                        XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION)) {
344                    throw new XmlPullParserException("Unexpected section under Provider: "
345                            + in.getName());
346                }
347                config = PasspointXmlUtils.deserializePasspointConfiguration(in,
348                        outerTagDepth + 1);
349            }
350        }
351        if (providerId == Long.MIN_VALUE) {
352            throw new XmlPullParserException("Missing provider ID");
353        }
354        if (config == null) {
355            throw new XmlPullParserException("Missing Passpoint configuration");
356        }
357        return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid,
358                caCertificateAlias, clientCertificateAlias, clientPrivateKeyAlias,
359                hasEverConnected, shared);
360    }
361
362    /**
363     * Reset share data (system wide Passpoint configurations).
364     */
365    private void resetShareData() {
366        mDataSource.setProviderIndex(0);
367    }
368
369    /**
370     * Reset user data (user specific Passpoint configurations).
371     */
372    private void resetUserData() {
373        mDataSource.setProviders(new ArrayList<PasspointProvider>());
374    }
375}
376
377