1 /*
2  * Copyright (C) 2020 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.annotation.NonNull;
20 import android.util.Log;
21 import android.util.SparseBooleanArray;
22 
23 import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
24 import com.android.server.wifi.util.XmlUtil;
25 
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 import org.xmlpull.v1.XmlSerializer;
29 
30 import java.io.IOException;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * This class performs serialization and parsing of XML data block that contain the map of carrier
36  * Wi-Fi offloading settings info.
37  */
38 public class WifiCarrierInfoStoreManagerData implements WifiConfigStore.StoreData {
39     private static final String TAG = "WifiCarrierInfoStoreManagerData";
40     private static final String XML_TAG_SECTION_HEADER =
41             "WifiCarrierInfoStoreManagerDataStores";
42     private static final String XML_TAG_MERGED_CARRIER_NETWORK_OFFLOAD_MAP =
43             "MergedCarrierNetworkOffloadMap";
44     private static final String XML_TAG_UNMERGED_CARRIER_NETWORK_OFFLOAD_MAP =
45             "UnmergedCarrierNetworkOffloadMap";
46 
47     private static final String XML_TAG_AUTO_JOIN_FLIPPED_ON_OOB_PSEUDONYM_ENABLED =
48             "AutoJoinFlippedOnOobPseudonymEnabled";
49 
50     /**
51      * Interface define the data source for the carrier IMSI protection exemption map store data.
52      */
53     public interface DataSource {
54 
55         /**
56          * Retrieve the merged carrier network offload map from the data source to serialize to
57          * disk.
58          *
59          * @param isMerged true for merged map, false for unmerged map.
60          */
getCarrierNetworkOffloadMap(boolean isMerged)61         SparseBooleanArray getCarrierNetworkOffloadMap(boolean isMerged);
62 
63         /**
64          * Should be called when serialize is completed.
65          */
serializeComplete()66         void serializeComplete();
67 
68 
69         /**
70          * Set the merged carrier network offload map in the data source after deserialize them
71          * from disk.
72          * @param isMerged true for merged map, false for unmerged map.
73          */
setCarrierNetworkOffloadMap(SparseBooleanArray carrierOffloadMap, boolean isMerged)74         void setCarrierNetworkOffloadMap(SparseBooleanArray carrierOffloadMap, boolean isMerged);
75 
76         /**
77          * Load the value which indicates if the auto-join flipping had been done if the OOB
78          * Pseudonym feature is enabled.
79          */
setAutoJoinFlippedOnOobPseudonymEnabled(boolean autoJoinFlipped)80         void setAutoJoinFlippedOnOobPseudonymEnabled(boolean autoJoinFlipped);
81 
82         /**
83          * Retrieve the value which indicates if the auto-join flipping had been done if the OOB
84          * Pseudonym feature is enabled.
85          */
getAutoJoinFlippedOnOobPseudonymEnabled()86         boolean getAutoJoinFlippedOnOobPseudonymEnabled();
87 
88         /**
89          * Clear internal data structure in preparation for user switch or initial store read.
90          */
reset()91         void reset();
92 
93         /**
94          * Indicates whether there is new data to serialize.
95          */
hasNewDataToSerialize()96         boolean hasNewDataToSerialize();
97     }
98 
99     private final DataSource mDataSource;
100 
101     /**
102      * Set the data source fot store data.
103      */
WifiCarrierInfoStoreManagerData(@onNull DataSource dataSource)104     public WifiCarrierInfoStoreManagerData(@NonNull DataSource dataSource) {
105         mDataSource = dataSource;
106     }
107 
108     @Override
serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil)109     public void serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil)
110             throws XmlPullParserException, IOException {
111         XmlUtil.writeNextValue(out, XML_TAG_MERGED_CARRIER_NETWORK_OFFLOAD_MAP,
112                 sparseArrayToStringMap(mDataSource.getCarrierNetworkOffloadMap(true)));
113         XmlUtil.writeNextValue(out, XML_TAG_UNMERGED_CARRIER_NETWORK_OFFLOAD_MAP,
114                 sparseArrayToStringMap(mDataSource.getCarrierNetworkOffloadMap(false)));
115         XmlUtil.writeNextValue(out, XML_TAG_AUTO_JOIN_FLIPPED_ON_OOB_PSEUDONYM_ENABLED,
116                 mDataSource.getAutoJoinFlippedOnOobPseudonymEnabled());
117         mDataSource.serializeComplete();
118     }
119 
120     @Override
deserializeData(XmlPullParser in, int outerTagDepth, int version, WifiConfigStoreEncryptionUtil encryptionUtil)121     public void deserializeData(XmlPullParser in, int outerTagDepth, int version,
122             WifiConfigStoreEncryptionUtil encryptionUtil)
123             throws XmlPullParserException, IOException {
124         mDataSource.reset();
125         if (in != null) {
126             parseWifiCarrierInfoStoreMaps(in, outerTagDepth);
127         }
128     }
129 
parseWifiCarrierInfoStoreMaps(XmlPullParser in, int outerTagDepth)130     private void parseWifiCarrierInfoStoreMaps(XmlPullParser in,
131             int outerTagDepth)
132             throws XmlPullParserException, IOException {
133         while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
134             String[] valueName = new String[1];
135             Object value = XmlUtil.readCurrentValue(in, valueName);
136             if (valueName[0] == null) {
137                 throw new XmlPullParserException("Missing value name");
138             }
139             switch (valueName[0]) {
140                 case XML_TAG_MERGED_CARRIER_NETWORK_OFFLOAD_MAP:
141                     if (value instanceof Map) {
142                         mDataSource.setCarrierNetworkOffloadMap(
143                                 stringMapToSparseArray((Map<String, Boolean>) value), true);
144                     }
145                     break;
146                 case XML_TAG_UNMERGED_CARRIER_NETWORK_OFFLOAD_MAP:
147                     if (value instanceof Map) {
148                         mDataSource.setCarrierNetworkOffloadMap(
149                                 stringMapToSparseArray((Map<String, Boolean>) value), false);
150                     }
151                     break;
152                 case XML_TAG_AUTO_JOIN_FLIPPED_ON_OOB_PSEUDONYM_ENABLED:
153                     if (value instanceof Boolean) {
154                         mDataSource.setAutoJoinFlippedOnOobPseudonymEnabled((Boolean) value);
155                     }
156                     break;
157                 default:
158                     Log.w(TAG, "Unknown tag under "
159                             + XML_TAG_SECTION_HEADER
160                             + ": " + valueName[0]);
161                     break;
162             }
163         }
164     }
165 
sparseArrayToStringMap(SparseBooleanArray input)166     private Map<String, Boolean> sparseArrayToStringMap(SparseBooleanArray input) {
167         Map<String, Boolean> output = new HashMap<>();
168         if (input == null) {
169             return output;
170         }
171         for (int i = 0; i < input.size(); i++) {
172             output.put(Integer.toString(input.keyAt(i)), input.valueAt(i));
173         }
174         return output;
175     }
176 
stringMapToSparseArray(Map<String, Boolean> input)177     private SparseBooleanArray stringMapToSparseArray(Map<String, Boolean> input) {
178         SparseBooleanArray output = new SparseBooleanArray();
179         if (input == null) {
180             return output;
181         }
182         for (Map.Entry<String, Boolean> entry : input.entrySet()) {
183             try {
184                 output.put(Integer.valueOf(entry.getKey()), entry.getValue());
185             } catch (NumberFormatException e) {
186                 Log.e(TAG, "Failed to Integer convert: " + entry.getKey());
187             }
188         }
189         return output;
190     }
191 
192     @Override
resetData()193     public void resetData() {
194         mDataSource.reset();
195     }
196 
197     @Override
hasNewDataToSerialize()198     public boolean hasNewDataToSerialize() {
199         return mDataSource.hasNewDataToSerialize();
200     }
201 
202     @Override
getName()203     public String getName() {
204         return XML_TAG_SECTION_HEADER;
205     }
206 
207     @Override
getStoreFileId()208     public int getStoreFileId() {
209         // User general store.
210         return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
211     }
212 }
213