Merge "Add WakeupConfigStoreData for reading and writing to config store."
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
new file mode 100644
index 0000000..f839ac8
--- /dev/null
+++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
@@ -0,0 +1,177 @@
+/*
+ * 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.util.ArraySet;
+
+import com.android.server.wifi.WifiConfigStore.StoreData;
+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_IS_ACTIVE = "IsActive";
+    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 final DataSource<Boolean> mIsActiveDataSource;
+    private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    /**
+     * Interface defining a data source for the store data.
+     */
+    interface DataSource<T> {
+        /**
+         * 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<Boolean> isActiveDataSource,
+            DataSource<Set<ScanResultMatchInfo>> networkDataSource) {
+        mIsActiveDataSource = isActiveDataSource;
+        mNetworkDataSource = networkDataSource;
+    }
+
+    @Override
+    public void serializeData(XmlSerializer out, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Share data not supported");
+        }
+
+        XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData());
+
+        for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) {
+            writeNetwork(out, scanResultMatchInfo);
+        }
+    }
+
+    /**
+     * Writes a {@link ScanResultMatchInfo} to an XML output stream.
+     *
+     * @param out XML output stream
+     * @param scanResultMatchInfo The ScanResultMatchInfo to serizialize
+     * @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);
+        XmlUtil.writeNextValue(out, XML_TAG_SECURITY, scanResultMatchInfo.networkType);
+
+        XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION);
+    }
+
+    @Override
+    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Shared data not supported");
+        }
+
+        boolean isActive = (Boolean) XmlUtil.readNextValueWithName(in, XML_TAG_IS_ACTIVE);
+        mIsActiveDataSource.setData(isActive);
+
+        Set<ScanResultMatchInfo> networks = new ArraySet<>();
+        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_NETWORK_SECTION, outerTagDepth)) {
+            networks.add(parseNetwork(in, outerTagDepth + 1));
+        }
+
+        mNetworkDataSource.setData(networks);
+    }
+
+    /**
+     * 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)) {
+            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:
+                    scanResultMatchInfo.networkType = (int) value;
+                    break;
+                default:
+                    throw new XmlPullParserException("Unknown tag under " + TAG + ": "
+                            + valueName[0]);
+            }
+        }
+
+        return scanResultMatchInfo;
+    }
+
+    @Override
+    public void resetData(boolean shared) {
+        if (!shared) {
+            mNetworkDataSource.setData(Collections.emptySet());
+            mIsActiveDataSource.setData(false);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return TAG;
+    }
+
+    @Override
+    public boolean supportShareData() {
+        return false;
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
new file mode 100644
index 0000000..6db9cf5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link WakeupConfigStoreData}.
+ */
+public class WakeupConfigStoreDataTest {
+
+    @Mock private WakeupConfigStoreData.DataSource<Boolean> mActiveDataSource;
+    @Mock private WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    private WakeupConfigStoreData mWakeupConfigData;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWakeupConfigData = new WakeupConfigStoreData(mActiveDataSource, mNetworkDataSource);
+    }
+
+    /**
+     * Helper function for serializing configuration data to a XML block.
+     *
+     * @param shared Flag indicating serializing shared or user configurations
+     * @return byte[] of the XML data
+     */
+    private byte[] serializeData(boolean shared) throws Exception {
+        final XmlSerializer out = new FastXmlSerializer();
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.serializeData(out, shared);
+        out.flush();
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * Helper function for parsing configuration data from a XML block.
+     *
+     * @param data XML data to parse from
+     * @param shared Flag indicating parsing of shared or user configurations
+     */
+    private void deserializeData(byte[] data, boolean shared) throws Exception {
+        final XmlPullParser in = Xml.newPullParser();
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+        in.setInput(inputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.deserializeData(in, in.getDepth(), shared);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to serialize data
+     * to the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void serializeShareDataThrowsException() throws Exception {
+        serializeData(true /* shared */);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to deserialize
+     * data from the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void deserializeShareDataThrowsException() throws Exception {
+        deserializeData(new byte[0], true /* shared */);
+    }
+
+    /**
+     * Can correctly serialize and deserialize the empty set case.
+     */
+    @Test
+    public void deserializeSerializedData_emptySet() throws Exception {
+        Set<ScanResultMatchInfo> networks = Collections.emptySet();
+        boolean isActive = false;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Can correctly serialize and deserialize a standard working case.
+     */
+    @Test
+    public void deserializeSerializedData() throws Exception {
+        ScanResultMatchInfo network1 = new ScanResultMatchInfo();
+        network1.networkSsid = "ssid 1";
+        network1.networkType = 0;
+
+        ScanResultMatchInfo network2 = new ScanResultMatchInfo();
+        network2.networkSsid = ",.23;4@, .#,%(,";
+        network2.networkType = 1;
+
+        ScanResultMatchInfo network3 = new ScanResultMatchInfo();
+        network3.networkSsid = "";
+        network3.networkType = 2;
+
+        Set<ScanResultMatchInfo> networks = Sets.newArraySet(network1, network2, network3);
+        boolean isActive = true;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Verify that reset data wipes the data sources.
+     */
+    @Test
+    public void resetDataWipesDataSources() {
+        mWakeupConfigData.resetData(false /* shared */);
+
+        verify(mActiveDataSource).setData(false);
+        verify(mNetworkDataSource).setData(eq(Collections.emptySet()));
+    }
+}