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
70    private final WifiKeyStore mKeyStore;
71    private final SIMAccessor mSimAccessor;
72    private final DataSource mDataSource;
73
74    /**
75     * Interface define the data source for the Passpoint configuration store data.
76     */
77    public interface DataSource {
78        /**
79         * Retrieve the provider list from the data source.
80         *
81         * @return List of {@link PasspointProvider}
82         */
83        List<PasspointProvider> getProviders();
84
85        /**
86         * Set the provider list in the data source.
87         *
88         * @param providers The list of providers
89         */
90        void setProviders(List<PasspointProvider> providers);
91
92        /**
93         * Retrieve the current provider index.
94         *
95         * @return long
96         */
97        long getProviderIndex();
98
99        /**
100         * Set the current provider index.
101         *
102         * @param providerIndex The provider index used for provider creation
103         */
104        void setProviderIndex(long providerIndex);
105    }
106
107    PasspointConfigStoreData(WifiKeyStore keyStore, SIMAccessor simAccessor,
108            DataSource dataSource) {
109        mKeyStore = keyStore;
110        mSimAccessor = simAccessor;
111        mDataSource = dataSource;
112    }
113
114    @Override
115    public void serializeData(XmlSerializer out, boolean shared)
116            throws XmlPullParserException, IOException {
117        if (shared) {
118            serializeShareData(out);
119        } else {
120            serializeUserData(out);
121        }
122    }
123
124    @Override
125    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
126            throws XmlPullParserException, IOException {
127        if (shared) {
128            deserializeShareData(in, outerTagDepth);
129        } else {
130            deserializeUserData(in, outerTagDepth);
131        }
132    }
133
134    @Override
135    public void resetData(boolean shared) {
136        if (shared) {
137            resetShareData();
138        } else {
139            resetUserData();
140        }
141    }
142
143    @Override
144    public String getName() {
145        return XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA;
146    }
147
148    @Override
149    public boolean supportShareData() {
150        return true;
151    }
152
153    /**
154     * Serialize share data (system wide Passpoint configurations) to a XML block.
155     *
156     * @param out The output stream to serialize data to
157     * @throws XmlPullParserException
158     * @throws IOException
159     */
160    private void serializeShareData(XmlSerializer out) throws XmlPullParserException, IOException {
161        XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_INDEX, mDataSource.getProviderIndex());
162    }
163
164    /**
165     * Serialize user data (user specific Passpoint configurations) to a XML block.
166     *
167     * @param out The output stream to serialize data to
168     * @throws XmlPullParserException
169     * @throws IOException
170     */
171    private void serializeUserData(XmlSerializer out) throws XmlPullParserException, IOException {
172        serializeProviderList(out, mDataSource.getProviders());
173    }
174
175    /**
176     * Serialize the list of Passpoint providers from the data source to a XML block.
177     *
178     * @param out The output stream to serialize data to
179     * @param providerList The list of providers to serialize
180     * @throws XmlPullParserException
181     * @throws IOException
182     */
183    private void serializeProviderList(XmlSerializer out, List<PasspointProvider> providerList)
184            throws XmlPullParserException, IOException {
185        if (providerList == null) {
186            return;
187        }
188        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
189        for (PasspointProvider provider : providerList) {
190            serializeProvider(out, provider);
191        }
192        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
193    }
194
195    /**
196     * Serialize a Passpoint provider to a XML block.
197     *
198     * @param out The output stream to serialize data to
199     * @param provider The provider to serialize
200     * @throws XmlPullParserException
201     * @throws IOException
202     */
203    private void serializeProvider(XmlSerializer out, PasspointProvider provider)
204            throws XmlPullParserException, IOException {
205        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
206        XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_ID, provider.getProviderId());
207        XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, provider.getCreatorUid());
208        XmlUtil.writeNextValue(out, XML_TAG_CA_CERTIFICATE_ALIAS,
209                provider.getCaCertificateAlias());
210        XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERTIFICATE_ALIAS,
211                provider.getClientCertificateAlias());
212        XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS,
213                provider.getClientPrivateKeyAlias());
214        if (provider.getConfig() != null) {
215            XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
216            PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig());
217            XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
218        }
219        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
220    }
221
222    /**
223     * Deserialize share data (system wide Passpoint configurations) from the input stream.
224     *
225     * @param in The input stream to read data from
226     * @param outerTagDepth The tag depth of the current XML section
227     * @throws XmlPullParserException
228     * @throws IOException
229     */
230    private void deserializeShareData(XmlPullParser in, int outerTagDepth)
231            throws XmlPullParserException, IOException {
232        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
233            String[] valueName = new String[1];
234            Object value = XmlUtil.readCurrentValue(in, valueName);
235            if (valueName[0] == null) {
236                throw new XmlPullParserException("Missing value name");
237            }
238            switch (valueName[0]) {
239                case XML_TAG_PROVIDER_INDEX:
240                    mDataSource.setProviderIndex((long) value);
241                    break;
242                default:
243                    throw new XmlPullParserException("Unknown value under share store data "
244                            + valueName[0]);
245            }
246        }
247    }
248
249    /**
250     * Deserialize user data (user specific Passpoint configurations) from the input stream.
251     *
252     * @param in The input stream to read data from
253     * @param outerTagDepth The tag depth of the current XML section
254     * @throws XmlPullParserException
255     * @throws IOException
256     */
257    private void deserializeUserData(XmlPullParser in, int outerTagDepth)
258            throws XmlPullParserException, IOException {
259        String[] headerName = new String[1];
260        while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
261            switch (headerName[0]) {
262                case XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST:
263                    mDataSource.setProviders(deserializeProviderList(in, outerTagDepth + 1));
264                    break;
265                default:
266                    throw new XmlPullParserException("Unknown Passpoint user store data "
267                            + headerName[0]);
268            }
269        }
270    }
271
272    /**
273     * Deserialize a list of Passpoint providers from the input stream.
274     *
275     * @param in The input stream to read data form
276     * @param outerTagDepth The tag depth of the current XML section
277     * @return List of {@link PasspointProvider}
278     * @throws XmlPullParserException
279     * @throws IOException
280     */
281    private List<PasspointProvider> deserializeProviderList(XmlPullParser in, int outerTagDepth)
282            throws XmlPullParserException, IOException {
283        List<PasspointProvider> providerList = new ArrayList<>();
284        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER,
285                outerTagDepth)) {
286            providerList.add(deserializeProvider(in, outerTagDepth + 1));
287        }
288        return providerList;
289    }
290
291    /**
292     * Deserialize a Passpoint provider from the input stream.
293     *
294     * @param in The input stream to read data from
295     * @param outerTagDepth The tag depth of the current XML section
296     * @return {@link PasspointProvider}
297     * @throws XmlPullParserException
298     * @throws IOException
299     */
300    private PasspointProvider deserializeProvider(XmlPullParser in, int outerTagDepth)
301            throws XmlPullParserException, IOException {
302        long providerId = Long.MIN_VALUE;
303        int creatorUid = Integer.MIN_VALUE;
304        String caCertificateAlias = null;
305        String clientCertificateAlias = null;
306        String clientPrivateKeyAlias = null;
307        PasspointConfiguration config = null;
308        while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
309            if (in.getAttributeValue(null, "name") != null) {
310                // Value elements.
311                String[] name = new String[1];
312                Object value = XmlUtil.readCurrentValue(in, name);
313                switch (name[0]) {
314                    case XML_TAG_PROVIDER_ID:
315                        providerId = (long) value;
316                        break;
317                    case XML_TAG_CREATOR_UID:
318                        creatorUid = (int) value;
319                        break;
320                    case XML_TAG_CA_CERTIFICATE_ALIAS:
321                        caCertificateAlias = (String) value;
322                        break;
323                    case XML_TAG_CLIENT_CERTIFICATE_ALIAS:
324                        clientCertificateAlias = (String) value;
325                        break;
326                    case XML_TAG_CLIENT_PRIVATE_KEY_ALIAS:
327                        clientPrivateKeyAlias = (String) value;
328                        break;
329                }
330            } else {
331                if (!TextUtils.equals(in.getName(),
332                        XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION)) {
333                    throw new XmlPullParserException("Unexpected section under Provider: "
334                            + in.getName());
335                }
336                config = PasspointXmlUtils.deserializePasspointConfiguration(in,
337                        outerTagDepth + 1);
338            }
339        }
340        if (providerId == Long.MIN_VALUE) {
341            throw new XmlPullParserException("Missing provider ID");
342        }
343        if (config == null) {
344            throw new XmlPullParserException("Missing Passpoint configuration");
345        }
346        return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid,
347                caCertificateAlias, clientCertificateAlias, clientPrivateKeyAlias);
348    }
349
350    /**
351     * Reset share data (system wide Passpoint configurations).
352     */
353    private void resetShareData() {
354        mDataSource.setProviderIndex(0);
355    }
356
357    /**
358     * Reset user data (user specific Passpoint configurations).
359     */
360    private void resetUserData() {
361        mDataSource.setProviders(new ArrayList<PasspointProvider>());
362    }
363}
364
365