1/*
2 * Copyright 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.util.ArraySet;
20import android.util.Log;
21
22import com.android.server.wifi.WifiConfigStore.StoreData;
23import com.android.server.wifi.util.XmlUtil;
24
25import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27import org.xmlpull.v1.XmlSerializer;
28
29import java.io.IOException;
30import java.util.Collections;
31import java.util.Set;
32
33/**
34 * Config store data for Wifi Wake.
35 */
36public class WakeupConfigStoreData implements StoreData {
37    private static final String TAG = "WakeupConfigStoreData";
38
39    private static final String XML_TAG_FEATURE_STATE_SECTION = "FeatureState";
40    private static final String XML_TAG_IS_ACTIVE = "IsActive";
41    private static final String XML_TAG_IS_ONBOARDED = "IsOnboarded";
42    private static final String XML_TAG_NOTIFICATIONS_SHOWN = "NotificationsShown";
43    private static final String XML_TAG_NETWORK_SECTION = "Network";
44    private static final String XML_TAG_SSID = "SSID";
45    private static final String XML_TAG_SECURITY = "Security";
46
47    private final DataSource<Boolean> mIsActiveDataSource;
48    private final DataSource<Boolean> mIsOnboardedDataSource;
49    private final DataSource<Integer> mNotificationsDataSource;
50    private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
51    private boolean mHasBeenRead = false;
52
53    /**
54     * Interface defining a data source for the store data.
55     *
56     * @param <T> Type of data source
57     */
58    public interface DataSource<T> {
59        /**
60         * Returns the data from the data source.
61         */
62        T getData();
63
64        /**
65         * Updates the data in the data source.
66         *
67         * @param data Data retrieved from the store
68         */
69        void setData(T data);
70    }
71
72    /**
73     * Creates the config store data with its data sources.
74     *
75     * @param isActiveDataSource Data source for isActive
76     * @param networkDataSource Data source for the locked network list
77     */
78    public WakeupConfigStoreData(
79            DataSource<Boolean> isActiveDataSource,
80            DataSource<Boolean> isOnboardedDataSource,
81            DataSource<Integer> notificationsDataSource,
82            DataSource<Set<ScanResultMatchInfo>> networkDataSource) {
83        mIsActiveDataSource = isActiveDataSource;
84        mIsOnboardedDataSource = isOnboardedDataSource;
85        mNotificationsDataSource = notificationsDataSource;
86        mNetworkDataSource = networkDataSource;
87    }
88
89    /**
90     * Returns whether the user store has been read.
91     */
92    public boolean hasBeenRead() {
93        return mHasBeenRead;
94    }
95
96    @Override
97    public void serializeData(XmlSerializer out, boolean shared)
98            throws XmlPullParserException, IOException {
99        if (shared) {
100            throw new XmlPullParserException("Share data not supported");
101        }
102
103        writeFeatureState(out);
104
105        for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) {
106            writeNetwork(out, scanResultMatchInfo);
107        }
108    }
109
110    /**
111     * Writes the current state of Wifi Wake to an XML output stream.
112     *
113     * @param out XML output stream
114     * @throws XmlPullParserException
115     * @throws IOException
116     */
117    private void writeFeatureState(XmlSerializer out)
118            throws IOException, XmlPullParserException {
119        XmlUtil.writeNextSectionStart(out, XML_TAG_FEATURE_STATE_SECTION);
120
121        XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData());
122        XmlUtil.writeNextValue(out, XML_TAG_IS_ONBOARDED, mIsOnboardedDataSource.getData());
123        XmlUtil.writeNextValue(out, XML_TAG_NOTIFICATIONS_SHOWN,
124                mNotificationsDataSource.getData());
125
126        XmlUtil.writeNextSectionEnd(out, XML_TAG_FEATURE_STATE_SECTION);
127    }
128
129    /**
130     * Writes a {@link ScanResultMatchInfo} to an XML output stream.
131     *
132     * @param out XML output stream
133     * @param scanResultMatchInfo The ScanResultMatchInfo to serialize
134     * @throws XmlPullParserException
135     * @throws IOException
136     */
137    private void writeNetwork(XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo)
138            throws XmlPullParserException, IOException {
139        XmlUtil.writeNextSectionStart(out, XML_TAG_NETWORK_SECTION);
140
141        XmlUtil.writeNextValue(out, XML_TAG_SSID, scanResultMatchInfo.networkSsid);
142        XmlUtil.writeNextValue(out, XML_TAG_SECURITY, scanResultMatchInfo.networkType);
143
144        XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION);
145    }
146
147    @Override
148    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
149            throws XmlPullParserException, IOException {
150        if (!shared) {
151            if (!mHasBeenRead) {
152                Log.d(TAG, "WifiWake user data has been read");
153                mHasBeenRead = true;
154            }
155        }
156
157        // Ignore empty reads.
158        if (in == null) {
159            return;
160        }
161        if (shared) {
162            throw new XmlPullParserException("Shared data not supported");
163        }
164
165        Set<ScanResultMatchInfo> networks = new ArraySet<>();
166
167        String[] headerName = new String[1];
168        while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
169            switch (headerName[0]) {
170                case XML_TAG_FEATURE_STATE_SECTION:
171                    parseFeatureState(in, outerTagDepth + 1);
172                    break;
173                case XML_TAG_NETWORK_SECTION:
174                    networks.add(parseNetwork(in, outerTagDepth + 1));
175                    break;
176            }
177        }
178
179        mNetworkDataSource.setData(networks);
180    }
181
182    /**
183     * Parses the state of Wifi Wake from an XML input stream and sets the respective data sources.
184     *
185     * @param in XML input stream
186     * @param outerTagDepth XML tag depth of the containing section
187     * @throws IOException
188     * @throws XmlPullParserException
189     */
190    private void parseFeatureState(XmlPullParser in, int outerTagDepth)
191            throws IOException, XmlPullParserException {
192        boolean isActive = false;
193        boolean isOnboarded = false;
194        int notificationsShown = 0;
195
196        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
197            String[] valueName = new String[1];
198            Object value = XmlUtil.readCurrentValue(in, valueName);
199            if (valueName[0] == null) {
200                throw new XmlPullParserException("Missing value name");
201            }
202            switch (valueName[0]) {
203                case XML_TAG_IS_ACTIVE:
204                    isActive = (Boolean) value;
205                    break;
206                case XML_TAG_IS_ONBOARDED:
207                    isOnboarded = (Boolean) value;
208                    break;
209                case XML_TAG_NOTIFICATIONS_SHOWN:
210                    notificationsShown = (Integer) value;
211                    break;
212                default:
213                    throw new XmlPullParserException("Unknown value found: " + valueName[0]);
214            }
215        }
216
217        mIsActiveDataSource.setData(isActive);
218        mIsOnboardedDataSource.setData(isOnboarded);
219        mNotificationsDataSource.setData(notificationsShown);
220    }
221
222    /**
223     * Parses a {@link ScanResultMatchInfo} from an XML input stream.
224     *
225     * @param in XML input stream
226     * @param outerTagDepth XML tag depth of the containing section
227     * @return The {@link ScanResultMatchInfo}
228     * @throws IOException
229     * @throws XmlPullParserException
230     */
231    private ScanResultMatchInfo parseNetwork(XmlPullParser in, int outerTagDepth)
232            throws IOException, XmlPullParserException {
233        ScanResultMatchInfo scanResultMatchInfo = new ScanResultMatchInfo();
234        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
235            String[] valueName = new String[1];
236            Object value = XmlUtil.readCurrentValue(in, valueName);
237            if (valueName[0] == null) {
238                throw new XmlPullParserException("Missing value name");
239            }
240            switch (valueName[0]) {
241                case XML_TAG_SSID:
242                    scanResultMatchInfo.networkSsid = (String) value;
243                    break;
244                case XML_TAG_SECURITY:
245                    scanResultMatchInfo.networkType = (int) value;
246                    break;
247                default:
248                    throw new XmlPullParserException("Unknown tag under " + TAG + ": "
249                            + valueName[0]);
250            }
251        }
252
253        return scanResultMatchInfo;
254    }
255
256    @Override
257    public void resetData(boolean shared) {
258        if (!shared) {
259            mNetworkDataSource.setData(Collections.emptySet());
260            mIsActiveDataSource.setData(false);
261            mIsOnboardedDataSource.setData(false);
262            mNotificationsDataSource.setData(0);
263        }
264    }
265
266    @Override
267    public String getName() {
268        return TAG;
269    }
270
271    @Override
272    public boolean supportShareData() {
273        return false;
274    }
275}
276