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;
18
19import android.content.Context;
20import android.net.IpConfiguration;
21import android.net.wifi.WifiConfiguration;
22import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
23import android.net.wifi.WifiEnterpriseConfig;
24import android.os.Process;
25import android.util.Log;
26import android.util.Pair;
27
28import com.android.server.wifi.util.XmlUtil;
29import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil;
30import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil;
31import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
32import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
33
34import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36import org.xmlpull.v1.XmlSerializer;
37
38import java.io.IOException;
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 * This class performs serialization and parsing of XML data block that contain the list of WiFi
44 * network configurations (XML block data inside <NetworkList> tag).
45 */
46public class NetworkListStoreData implements WifiConfigStore.StoreData {
47    private static final String TAG = "NetworkListStoreData";
48
49    private static final String XML_TAG_SECTION_HEADER_NETWORK_LIST = "NetworkList";
50    private static final String XML_TAG_SECTION_HEADER_NETWORK = "Network";
51    private static final String XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION = "WifiConfiguration";
52    private static final String XML_TAG_SECTION_HEADER_NETWORK_STATUS = "NetworkStatus";
53    private static final String XML_TAG_SECTION_HEADER_IP_CONFIGURATION = "IpConfiguration";
54    private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION =
55            "WifiEnterpriseConfiguration";
56
57    private final Context mContext;
58
59    /**
60     * List of saved shared networks visible to all the users to be stored in the shared store file.
61     */
62    private List<WifiConfiguration> mSharedConfigurations;
63    /**
64     * List of saved private networks only visible to the current user to be stored in the user
65     * specific store file.
66     */
67    private List<WifiConfiguration> mUserConfigurations;
68
69    NetworkListStoreData(Context context) {
70        mContext = context;
71    }
72
73    @Override
74    public void serializeData(XmlSerializer out, boolean shared)
75            throws XmlPullParserException, IOException {
76        if (shared) {
77            serializeNetworkList(out, mSharedConfigurations);
78        } else {
79            serializeNetworkList(out, mUserConfigurations);
80        }
81    }
82
83    @Override
84    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
85            throws XmlPullParserException, IOException {
86        // Ignore empty reads.
87        if (in == null) {
88            return;
89        }
90        if (shared) {
91            mSharedConfigurations = parseNetworkList(in, outerTagDepth);
92        } else {
93            mUserConfigurations = parseNetworkList(in, outerTagDepth);
94        }
95    }
96
97    @Override
98    public void resetData(boolean shared) {
99        if (shared) {
100            mSharedConfigurations = null;
101        } else {
102            mUserConfigurations = null;
103        }
104    }
105
106    @Override
107    public String getName() {
108        return XML_TAG_SECTION_HEADER_NETWORK_LIST;
109    }
110
111    @Override
112    public boolean supportShareData() {
113        return true;
114    }
115
116    public void setSharedConfigurations(List<WifiConfiguration> configs) {
117        mSharedConfigurations = configs;
118    }
119
120    /**
121     * An empty list will be returned if no shared configurations.
122     *
123     * @return List of {@link WifiConfiguration}
124     */
125    public List<WifiConfiguration> getSharedConfigurations() {
126        if (mSharedConfigurations == null) {
127            return new ArrayList<WifiConfiguration>();
128        }
129        return mSharedConfigurations;
130    }
131
132    public void setUserConfigurations(List<WifiConfiguration> configs) {
133        mUserConfigurations = configs;
134    }
135
136    /**
137     * An empty list will be returned if no user configurations.
138     *
139     * @return List of {@link WifiConfiguration}
140     */
141    public List<WifiConfiguration> getUserConfigurations() {
142        if (mUserConfigurations == null) {
143            return new ArrayList<WifiConfiguration>();
144        }
145        return mUserConfigurations;
146    }
147
148    /**
149     * Serialize the list of {@link WifiConfiguration} to an output stream in XML format.
150     *
151     * @param out The output stream to serialize the data to
152     * @param networkList The network list to serialize
153     * @throws XmlPullParserException
154     * @throws IOException
155     */
156    private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList)
157            throws XmlPullParserException, IOException {
158        if (networkList == null) {
159            return;
160        }
161        for (WifiConfiguration network : networkList) {
162            serializeNetwork(out, network);
163        }
164    }
165
166    /**
167     * Serialize a {@link WifiConfiguration} to an output stream in XML format.
168     * @param out
169     * @param config
170     * @throws XmlPullParserException
171     * @throws IOException
172     */
173    private void serializeNetwork(XmlSerializer out, WifiConfiguration config)
174            throws XmlPullParserException, IOException {
175        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
176
177        // Serialize WifiConfiguration.
178        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
179        WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config);
180        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
181
182        // Serialize network selection status.
183        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
184        NetworkSelectionStatusXmlUtil.writeToXml(out, config.getNetworkSelectionStatus());
185        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
186
187        // Serialize IP configuration.
188        XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
189        IpConfigurationXmlUtil.writeToXml(out, config.getIpConfiguration());
190        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
191
192        // Serialize enterprise configuration for enterprise networks.
193        if (config.enterpriseConfig != null
194                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
195            XmlUtil.writeNextSectionStart(
196                    out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
197            WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig);
198            XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
199        }
200
201        XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK);
202    }
203
204    /**
205     * Parse a list of {@link WifiConfiguration} from an input stream in XML format.
206     *
207     * @param in The input stream to read from
208     * @param outerTagDepth The XML tag depth of the outer XML block
209     * @return List of {@link WifiConfiguration}
210     * @throws XmlPullParserException
211     * @throws IOException
212     */
213    private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth)
214            throws XmlPullParserException, IOException {
215        List<WifiConfiguration> networkList = new ArrayList<>();
216        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK,
217                outerTagDepth)) {
218            // Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
219            // fatal and should abort the entire loading process.
220            try {
221                WifiConfiguration config = parseNetwork(in, outerTagDepth + 1);
222                networkList.add(config);
223            } catch (RuntimeException e) {
224                // Failed to parse this network, skip it.
225                Log.e(TAG, "Failed to parse network config. Skipping...", e);
226            }
227        }
228        return networkList;
229    }
230
231    /**
232     * Parse a {@link WifiConfiguration} from an input stream in XML format.
233     *
234     * @param in The input stream to read from
235     * @param outerTagDepth The XML tag depth of the outer XML block
236     * @return {@link WifiConfiguration}
237     * @throws XmlPullParserException
238     * @throws IOException
239     */
240    private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth)
241            throws XmlPullParserException, IOException {
242        Pair<String, WifiConfiguration> parsedConfig = null;
243        NetworkSelectionStatus status = null;
244        IpConfiguration ipConfiguration = null;
245        WifiEnterpriseConfig enterpriseConfig = null;
246
247        String[] headerName = new String[1];
248        while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
249            switch (headerName[0]) {
250                case XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION:
251                    if (parsedConfig != null) {
252                        throw new XmlPullParserException("Detected duplicate tag for: "
253                                + XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
254                    }
255                    parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
256                    break;
257                case XML_TAG_SECTION_HEADER_NETWORK_STATUS:
258                    if (status != null) {
259                        throw new XmlPullParserException("Detected duplicate tag for: "
260                                + XML_TAG_SECTION_HEADER_NETWORK_STATUS);
261                    }
262                    status = NetworkSelectionStatusXmlUtil.parseFromXml(in, outerTagDepth + 1);
263                    break;
264                case XML_TAG_SECTION_HEADER_IP_CONFIGURATION:
265                    if (ipConfiguration != null) {
266                        throw new XmlPullParserException("Detected duplicate tag for: "
267                                + XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
268                    }
269                    ipConfiguration = IpConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
270                    break;
271                case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION:
272                    if (enterpriseConfig != null) {
273                        throw new XmlPullParserException("Detected duplicate tag for: "
274                                + XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
275                    }
276                    enterpriseConfig =
277                            WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1);
278                    break;
279                default:
280                    throw new XmlPullParserException("Unknown tag under "
281                            + XML_TAG_SECTION_HEADER_NETWORK + ": " + headerName[0]);
282            }
283        }
284        if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) {
285            throw new XmlPullParserException("XML parsing of wifi configuration failed");
286        }
287        String configKeyParsed = parsedConfig.first;
288        WifiConfiguration configuration = parsedConfig.second;
289        String configKeyCalculated = configuration.configKey();
290        if (!configKeyParsed.equals(configKeyCalculated)) {
291            throw new XmlPullParserException(
292                    "Configuration key does not match. Retrieved: " + configKeyParsed
293                            + ", Calculated: " + configKeyCalculated);
294        }
295        // Set creatorUid/creatorName for networks which don't have it set to valid value.
296        String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid);
297        if (creatorName == null) {
298            Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey()
299                    + ", creatorUid=" + configuration.creatorUid);
300            configuration.creatorUid = Process.SYSTEM_UID;
301            configuration.creatorName =
302                    mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID);
303        } else if (!creatorName.equals(configuration.creatorName)) {
304            Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey()
305                    + ", creatorUid=" + configuration.creatorUid
306                    + ", creatorName=" + configuration.creatorName);
307            configuration.creatorName = creatorName;
308        }
309
310        configuration.setNetworkSelectionStatus(status);
311        configuration.setIpConfiguration(ipConfiguration);
312        if (enterpriseConfig != null) {
313            configuration.enterpriseConfig = enterpriseConfig;
314        }
315        return configuration;
316    }
317}
318
319