blob: 763f267b949ea9863506fb55a7f0b39beb00d428 [file] [log] [blame]
/*
* Copyright (C) 2018 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;
import android.annotation.Nullable;
import android.net.MacAddress;
import android.util.Log;
import com.android.server.wifi.WifiNetworkFactory.AccessPoint;
import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
import com.android.server.wifi.util.XmlUtil;
import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* This class performs serialization and parsing of XML data block that contain the list of WiFi
* network request approvals.
*/
public class NetworkRequestStoreData implements WifiConfigStore.StoreData {
private static final String TAG = "NetworkRequestStoreData";
private static final String XML_TAG_SECTION_HEADER_NETWORK_REQUEST_MAP =
"NetworkRequestMap";
private static final String XML_TAG_SECTION_HEADER_APPROVED_ACCESS_POINTS_PER_APP =
"ApprovedAccessPointsPerApp";
private static final String XML_TAG_REQUESTOR_PACKAGE_NAME = "RequestorPackageName";
private static final String XML_TAG_SECTION_HEADER_ACCESS_POINT = "AccessPoint";
private static final String XML_TAG_ACCESS_POINT_SSID = WifiConfigurationXmlUtil.XML_TAG_SSID;
private static final String XML_TAG_ACCESS_POINT_BSSID = WifiConfigurationXmlUtil.XML_TAG_BSSID;
private static final String XML_TAG_ACCESS_POINT_NETWORK_TYPE = "NetworkType";
/**
* Interface define the data source for the network requests store data.
*/
public interface DataSource {
/**
* Retrieve the approved access points from the data source to serialize them to disk.
*
* @return Map of package name to a set of {@link AccessPoint}
*/
Map<String, Set<AccessPoint>> toSerialize();
/**
* Set the approved access points in the data source after serializing them from disk.
*
* @param approvedAccessPoints Map of package name to {@link AccessPoint}
*/
void fromDeserialized(Map<String, Set<AccessPoint>> approvedAccessPoints);
/**
* Clear internal data structure in preparation for user switch or initial store read.
*/
void reset();
/**
* Indicates whether there is new data to serialize.
*/
boolean hasNewDataToSerialize();
}
private final DataSource mDataSource;
public NetworkRequestStoreData(DataSource dataSource) {
mDataSource = dataSource;
}
@Override
public void serializeData(XmlSerializer out,
@Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
throws XmlPullParserException, IOException {
serializeApprovedAccessPointsMap(out, mDataSource.toSerialize());
}
@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;
}
mDataSource.fromDeserialized(parseApprovedAccessPointsMap(in, outerTagDepth));
}
@Override
public void resetData() {
mDataSource.reset();
}
@Override
public boolean hasNewDataToSerialize() {
return mDataSource.hasNewDataToSerialize();
}
@Override
public String getName() {
return XML_TAG_SECTION_HEADER_NETWORK_REQUEST_MAP;
}
@Override
public @WifiConfigStore.StoreFileId int getStoreFileId() {
return WifiConfigStore.STORE_FILE_USER_GENERAL;
}
/**
* Serialize the map of package name to approved access points to an output stream in XML
* format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private void serializeApprovedAccessPointsMap(
XmlSerializer out, final Map<String, Set<AccessPoint>> approvedAccessPointsMap)
throws XmlPullParserException, IOException {
if (approvedAccessPointsMap == null) {
return;
}
for (Entry<String, Set<AccessPoint>> entry : approvedAccessPointsMap.entrySet()) {
String packageName = entry.getKey();
XmlUtil.writeNextSectionStart(out,
XML_TAG_SECTION_HEADER_APPROVED_ACCESS_POINTS_PER_APP);
XmlUtil.writeNextValue(out, XML_TAG_REQUESTOR_PACKAGE_NAME, packageName);
serializeApprovedAccessPoints(out, entry.getValue());
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_APPROVED_ACCESS_POINTS_PER_APP);
}
}
/**
* Serialize the set of approved access points to an output stream in XML format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private void serializeApprovedAccessPoints(
XmlSerializer out, final Set<AccessPoint> approvedAccessPoints)
throws XmlPullParserException, IOException {
for (AccessPoint approvedAccessPoint : approvedAccessPoints) {
serializeApprovedAccessPoint(out, approvedAccessPoint);
}
}
/**
* Serialize a {@link AccessPoint} to an output stream in XML format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private void serializeApprovedAccessPoint(XmlSerializer out,
AccessPoint approvedAccessPoint)
throws XmlPullParserException, IOException {
XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_ACCESS_POINT);
XmlUtil.writeNextValue(out, XML_TAG_ACCESS_POINT_SSID, approvedAccessPoint.ssid);
XmlUtil.writeNextValue(out, XML_TAG_ACCESS_POINT_BSSID,
approvedAccessPoint.bssid.toString());
XmlUtil.writeNextValue(out, XML_TAG_ACCESS_POINT_NETWORK_TYPE,
approvedAccessPoint.networkType);
XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_ACCESS_POINT);
}
/**
* Parse a map of package name to approved access point from an input stream in XML format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private Map<String, Set<AccessPoint>> parseApprovedAccessPointsMap(XmlPullParser in,
int outerTagDepth)
throws XmlPullParserException, IOException {
Map<String, Set<AccessPoint>> approvedAccessPointsMap = new HashMap<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
in, XML_TAG_SECTION_HEADER_APPROVED_ACCESS_POINTS_PER_APP, outerTagDepth)) {
// Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
// fatal and should abort the entire loading process.
try {
String packageName =
(String) XmlUtil.readNextValueWithName(in, XML_TAG_REQUESTOR_PACKAGE_NAME);
Set<AccessPoint> approvedAccessPoints =
parseApprovedAccessPoints(in, outerTagDepth + 1);
approvedAccessPointsMap.put(packageName, approvedAccessPoints);
} catch (RuntimeException e) {
// Failed to parse this network, skip it.
Log.e(TAG, "Failed to parse network suggestion. Skipping...", e);
}
}
return approvedAccessPointsMap;
}
/**
* Parse a set of approved access points from an input stream in XML format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private Set<AccessPoint> parseApprovedAccessPoints(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
Set<AccessPoint> approvedAccessPoints = new LinkedHashSet<>();
while (XmlUtil.gotoNextSectionWithNameOrEnd(
in, XML_TAG_SECTION_HEADER_ACCESS_POINT, outerTagDepth)) {
// Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
// fatal and should abort the entire loading process.
try {
AccessPoint approvedAccessPoint =
parseApprovedAccessPoint(in, outerTagDepth + 1);
approvedAccessPoints.add(approvedAccessPoint);
} catch (RuntimeException e) {
// Failed to parse this network, skip it.
Log.e(TAG, "Failed to parse network suggestion. Skipping...", e);
}
}
return approvedAccessPoints;
}
/**
* Parse a {@link AccessPoint} from an input stream in XML format.
*
* @throws XmlPullParserException
* @throws IOException
*/
private AccessPoint parseApprovedAccessPoint(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
String ssid = null;
MacAddress bssid = null;
int networkType = -1;
// Loop through and parse out all the elements from the stream within this section.
while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
String[] valueName = new String[1];
Object value = XmlUtil.readCurrentValue(in, valueName);
if (valueName[0] == null) {
throw new XmlPullParserException("Missing value name");
}
switch (valueName[0]) {
case XML_TAG_ACCESS_POINT_SSID:
ssid = (String) value;
break;
case XML_TAG_ACCESS_POINT_BSSID:
bssid = MacAddress.fromString((String) value);
break;
case XML_TAG_ACCESS_POINT_NETWORK_TYPE:
networkType = (int) value;
break;
default:
Log.w(TAG, "Ignoring unknown value name found: " + valueName[0]);
break;
}
}
if (ssid == null) {
throw new XmlPullParserException("XML parsing of ssid failed");
}
if (bssid == null) {
throw new XmlPullParserException("XML parsing of bssid failed");
}
if (networkType == -1) {
throw new XmlPullParserException("XML parsing of network type failed");
}
return new AccessPoint(ssid, bssid, networkType);
}
}