/* * Copyright 2017 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.wifi.SecurityParams; import android.util.ArraySet; import android.util.Log; import com.android.server.wifi.WifiConfigStore.StoreData; import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Collections; import java.util.Set; /** * Config store data for Wifi Wake. */ public class WakeupConfigStoreData implements StoreData { private static final String TAG = "WakeupConfigStoreData"; private static final String XML_TAG_FEATURE_STATE_SECTION = "FeatureState"; private static final String XML_TAG_IS_ACTIVE = "IsActive"; private static final String XML_TAG_IS_ONBOARDED = "IsOnboarded"; private static final String XML_TAG_NOTIFICATIONS_SHOWN = "NotificationsShown"; private static final String XML_TAG_NETWORK_SECTION = "Network"; private static final String XML_TAG_SSID = "SSID"; private static final String XML_TAG_SECURITY = "Security"; private static final String XML_TAG_SECURITY_PARAMS_LIST = "SecurityParamsList"; private static final String XML_TAG_SECURITY_PARAMS = "SecurityParams"; private static final String XML_TAG_SECURITY_TYPE = "SecurityType"; private static final String XML_TAG_SAE_IS_H2E_ONLY_MODE = "SaeIsH2eOnlyMode"; private static final String XML_TAG_SAE_IS_PK_ONLY_MODE = "SaeIsPkOnlyMode"; private static final String XML_TAG_IS_ADDED_BY_AUTO_UPGRADE = "IsAddedByAutoUpgrade"; private final DataSource mIsActiveDataSource; private final DataSource mIsOnboardedDataSource; private final DataSource mNotificationsDataSource; private final DataSource> mNetworkDataSource; private boolean mHasBeenRead = false; /** * Interface defining a data source for the store data. * * @param Type of data source */ public interface DataSource { /** * Returns the data from the data source. */ T getData(); /** * Updates the data in the data source. * * @param data Data retrieved from the store */ void setData(T data); } /** * Creates the config store data with its data sources. * * @param isActiveDataSource Data source for isActive * @param networkDataSource Data source for the locked network list */ public WakeupConfigStoreData( DataSource isActiveDataSource, DataSource isOnboardedDataSource, DataSource notificationsDataSource, DataSource> networkDataSource) { mIsActiveDataSource = isActiveDataSource; mIsOnboardedDataSource = isOnboardedDataSource; mNotificationsDataSource = notificationsDataSource; mNetworkDataSource = networkDataSource; } /** * Returns whether the user store has been read. */ public boolean hasBeenRead() { return mHasBeenRead; } @Override public void serializeData(XmlSerializer out, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { writeFeatureState(out); for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) { writeNetwork(out, scanResultMatchInfo); } } /** * Writes the current state of Wifi Wake to an XML output stream. * * @param out XML output stream * @throws XmlPullParserException * @throws IOException */ private void writeFeatureState(XmlSerializer out) throws IOException, XmlPullParserException { XmlUtil.writeNextSectionStart(out, XML_TAG_FEATURE_STATE_SECTION); XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData()); XmlUtil.writeNextValue(out, XML_TAG_IS_ONBOARDED, mIsOnboardedDataSource.getData()); XmlUtil.writeNextValue(out, XML_TAG_NOTIFICATIONS_SHOWN, mNotificationsDataSource.getData()); XmlUtil.writeNextSectionEnd(out, XML_TAG_FEATURE_STATE_SECTION); } private void writeSecurityParamsList( XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo) throws XmlPullParserException, IOException { XmlUtil.writeNextSectionStart(out, XML_TAG_SECURITY_PARAMS_LIST); for (SecurityParams params: scanResultMatchInfo.securityParamsList) { XmlUtil.writeNextSectionStart(out, XML_TAG_SECURITY_PARAMS); XmlUtil.writeNextValue( out, XML_TAG_SECURITY_TYPE, params.getSecurityType()); XmlUtil.writeNextValue( out, XML_TAG_SAE_IS_H2E_ONLY_MODE, params.isSaeH2eOnlyMode()); XmlUtil.writeNextValue( out, XML_TAG_SAE_IS_PK_ONLY_MODE, params.isSaePkOnlyMode()); XmlUtil.writeNextValue( out, XML_TAG_IS_ADDED_BY_AUTO_UPGRADE, params.isAddedByAutoUpgrade()); XmlUtil.writeNextSectionEnd(out, XML_TAG_SECURITY_PARAMS); } XmlUtil.writeNextSectionEnd(out, XML_TAG_SECURITY_PARAMS_LIST); } /** * Writes a {@link ScanResultMatchInfo} to an XML output stream. * * @param out XML output stream * @param scanResultMatchInfo The ScanResultMatchInfo to serialize * @throws XmlPullParserException * @throws IOException */ private void writeNetwork(XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo) throws XmlPullParserException, IOException { XmlUtil.writeNextSectionStart(out, XML_TAG_NETWORK_SECTION); XmlUtil.writeNextValue(out, XML_TAG_SSID, scanResultMatchInfo.networkSsid); writeSecurityParamsList(out, scanResultMatchInfo); XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION); } @Override public void deserializeData(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { if (!mHasBeenRead) { Log.d(TAG, "WifiWake user data has been read"); mHasBeenRead = true; } // Ignore empty reads. if (in == null) { return; } Set networks = new ArraySet<>(); String[] headerName = new String[1]; while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) { switch (headerName[0]) { case XML_TAG_FEATURE_STATE_SECTION: parseFeatureState(in, outerTagDepth + 1); break; case XML_TAG_NETWORK_SECTION: networks.add(parseNetwork(in, outerTagDepth + 1)); break; } } mNetworkDataSource.setData(networks); } /** * Parses the state of Wifi Wake from an XML input stream and sets the respective data sources. * * @param in XML input stream * @param outerTagDepth XML tag depth of the containing section * @throws IOException * @throws XmlPullParserException */ private void parseFeatureState(XmlPullParser in, int outerTagDepth) throws IOException, XmlPullParserException { boolean isActive = false; boolean isOnboarded = false; int notificationsShown = 0; 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_IS_ACTIVE: isActive = (Boolean) value; break; case XML_TAG_IS_ONBOARDED: isOnboarded = (Boolean) value; break; case XML_TAG_NOTIFICATIONS_SHOWN: notificationsShown = (Integer) value; break; default: Log.w(TAG, "Unknown value found: " + valueName[0]); break; } } mIsActiveDataSource.setData(isActive); mIsOnboardedDataSource.setData(isOnboarded); mNotificationsDataSource.setData(notificationsShown); } private SecurityParams parseSecurityParams(XmlPullParser in, int outerTagDepth) throws IOException, XmlPullParserException { SecurityParams params = null; while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { String[] valueName = new String[1]; Object value = XmlUtil.readCurrentValue(in, valueName); String tagName = valueName[0]; if (tagName == null) { throw new XmlPullParserException("Missing value name"); } switch (tagName) { case XML_TAG_SECURITY_TYPE: params = SecurityParams.createSecurityParamsBySecurityType((int) value); break; case XML_TAG_SAE_IS_H2E_ONLY_MODE: if (null == params) { throw new XmlPullParserException("Missing security type."); } params.enableSaeH2eOnlyMode((boolean) value); break; case XML_TAG_SAE_IS_PK_ONLY_MODE: if (null == params) { throw new XmlPullParserException("Missing security type."); } params.enableSaePkOnlyMode((boolean) value); break; case XML_TAG_IS_ADDED_BY_AUTO_UPGRADE: if (null == params) { throw new XmlPullParserException("Missing security type."); } params.setIsAddedByAutoUpgrade((boolean) value); break; } } return params; } private void parseSecurityParamsList( XmlPullParser in, int outerTagDepth, ScanResultMatchInfo scanResultMatchInfo) throws IOException, XmlPullParserException { while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { switch (in.getName()) { case XML_TAG_SECURITY_PARAMS: SecurityParams params = parseSecurityParams(in, outerTagDepth + 1); if (null != params) { scanResultMatchInfo.securityParamsList.add(params); } break; } } } /** * Parses a {@link ScanResultMatchInfo} from an XML input stream. * * @param in XML input stream * @param outerTagDepth XML tag depth of the containing section * @return The {@link ScanResultMatchInfo} * @throws IOException * @throws XmlPullParserException */ private ScanResultMatchInfo parseNetwork(XmlPullParser in, int outerTagDepth) throws IOException, XmlPullParserException { ScanResultMatchInfo scanResultMatchInfo = new ScanResultMatchInfo(); while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { if (in.getAttributeValue(null, "name") == null) { String tagName = in.getName(); if (tagName == null) { throw new XmlPullParserException("Unexpected null tag found"); } switch (tagName) { case XML_TAG_SECURITY_PARAMS_LIST: parseSecurityParamsList(in, outerTagDepth + 1, scanResultMatchInfo); break; } continue; } 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_SSID: scanResultMatchInfo.networkSsid = (String) value; break; case XML_TAG_SECURITY: // Migrate data from R to S. scanResultMatchInfo.securityParamsList.add( SecurityParams.createSecurityParamsBySecurityType((int) value)); break; default: Log.w(TAG, "Ignoring unknown tag under " + TAG + ": " + valueName[0]); break; } } return scanResultMatchInfo; } @Override public void resetData() { mNetworkDataSource.setData(Collections.emptySet()); mIsActiveDataSource.setData(false); mIsOnboardedDataSource.setData(false); mNotificationsDataSource.setData(0); } @Override public boolean hasNewDataToSerialize() { // always persist. return true; } @Override public String getName() { return TAG; } @Override public @WifiConfigStore.StoreFileId int getStoreFileId() { // Shared general store. return WifiConfigStore.STORE_FILE_USER_GENERAL; } }