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 17 package com.android.server.wifi; 18 19 import android.content.Context; 20 import android.net.IpConfiguration; 21 import android.net.wifi.WifiConfiguration; 22 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 23 import android.net.wifi.WifiEnterpriseConfig; 24 import android.os.Process; 25 import android.util.Log; 26 import android.util.Pair; 27 28 import com.android.server.wifi.util.XmlUtil; 29 import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil; 30 import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil; 31 import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; 32 import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil; 33 34 import org.xmlpull.v1.XmlPullParser; 35 import org.xmlpull.v1.XmlPullParserException; 36 import org.xmlpull.v1.XmlSerializer; 37 38 import java.io.IOException; 39 import java.util.ArrayList; 40 import 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 */ 46 public 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