• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
21 import android.content.Context;
22 import android.net.wifi.WifiMigration;
23 import android.os.Handler;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.server.wifi.util.SettingsMigrationDataHolder;
29 import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
30 import com.android.server.wifi.util.XmlUtil;
31 
32 import org.xmlpull.v1.XmlPullParser;
33 import org.xmlpull.v1.XmlPullParserException;
34 import org.xmlpull.v1.XmlSerializer;
35 
36 import java.io.FileDescriptor;
37 import java.io.IOException;
38 import java.io.PrintWriter;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.Map;
42 
43 /**
44  * Store data for storing wifi settings. These are key (string) / value pairs that are stored in
45  * WifiConfigStore.xml file in a separate section.
46  */
47 public class WifiSettingsConfigStore {
48     private static final String TAG = "WifiSettingsConfigStore";
49 
50     // List of all allowed keys.
51     private static final ArrayList<Key> sKeys = new ArrayList<>();
52 
53     /******** Wifi shared pref keys ***************/
54     /**
55      * Indicate whether factory reset request is pending.
56      */
57     public static final Key<Boolean> WIFI_P2P_PENDING_FACTORY_RESET =
58             new Key<>("wifi_p2p_pending_factory_reset", false);
59 
60     /**
61      * Allow scans to be enabled even wifi is turned off.
62      */
63     public static final Key<Boolean> WIFI_SCAN_ALWAYS_AVAILABLE =
64             new Key<>("wifi_scan_always_enabled", false);
65 
66     /**
67      * Whether wifi scan throttle is enabled or not.
68      */
69     public static final Key<Boolean> WIFI_SCAN_THROTTLE_ENABLED =
70             new Key<>("wifi_scan_throttle_enabled", true);
71 
72     /**
73      * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
74      * will enable it. In the future, additional values may be supported.
75      */
76     public static final Key<Boolean> WIFI_VERBOSE_LOGGING_ENABLED =
77             new Key<>("wifi_verbose_logging_enabled", false);
78 
79     /**
80      * The Wi-Fi peer-to-peer device name
81      */
82     public static final Key<String> WIFI_P2P_DEVICE_NAME =
83             new Key<>("wifi_p2p_device_name", null);
84 
85     /**
86      * Whether Wifi scoring is enabled or not.
87      */
88     public static final Key<Boolean> WIFI_SCORING_ENABLED =
89             new Key<>("wifi_scoring_enabled", true);
90 
91     /**
92      * Store the STA factory MAC address retrieved from the driver on the first bootup.
93      */
94     public static final Key<String> WIFI_STA_FACTORY_MAC_ADDRESS =
95             new Key<>("wifi_sta_factory_mac_address", null);
96 
97     /**
98      * Store the default country code updated via {@link WifiManager#setDefaultCountryCode(String)}
99      */
100     public static final Key<String> WIFI_DEFAULT_COUNTRY_CODE =
101             new Key<>("wifi_default_country_code", WifiCountryCode.getOemDefaultCountryCode());
102 
103     /******** Wifi shared pref keys ***************/
104 
105     private final Context mContext;
106     private final Handler mHandler;
107     private final SettingsMigrationDataHolder mSettingsMigrationDataHolder;
108     private final WifiConfigManager mWifiConfigManager;
109 
110     private final Object mLock = new Object();
111     @GuardedBy("mLock")
112     private final Map<String, Object> mSettings = new HashMap<>();
113     @GuardedBy("mLock")
114     private final Map<String, Map<OnSettingsChangedListener, Handler>> mListeners =
115             new HashMap<>();
116     private WifiMigration.SettingsMigrationData mCachedMigrationData = null;
117 
118     private boolean mHasNewDataToSerialize = false;
119 
120     /**
121      * Interface for a settings change listener.
122      * @param <T> Type of the value.
123      */
124     public interface OnSettingsChangedListener<T> {
125         /**
126          * Invoked when a particular key settings changes.
127          *
128          * @param key Key that was changed.
129          * @param newValue New value that was assigned to the key.
130          */
onSettingsChanged(@onNull Key<T> key, @Nullable T newValue)131         void onSettingsChanged(@NonNull Key<T> key, @Nullable T newValue);
132     }
133 
WifiSettingsConfigStore(@onNull Context context, @NonNull Handler handler, @NonNull SettingsMigrationDataHolder settingsMigrationDataHolder, @NonNull WifiConfigManager wifiConfigManager, @NonNull WifiConfigStore wifiConfigStore)134     public WifiSettingsConfigStore(@NonNull Context context, @NonNull Handler handler,
135             @NonNull SettingsMigrationDataHolder settingsMigrationDataHolder,
136             @NonNull WifiConfigManager wifiConfigManager,
137             @NonNull WifiConfigStore wifiConfigStore) {
138         mContext = context;
139         mHandler = handler;
140         mSettingsMigrationDataHolder = settingsMigrationDataHolder;
141         mWifiConfigManager = wifiConfigManager;
142 
143         // Register our data store.
144         wifiConfigStore.registerStoreData(new StoreData());
145     }
146 
invokeAllListeners()147     private void invokeAllListeners() {
148         synchronized (mLock) {
149             for (Key key : sKeys) {
150                 invokeListeners(key);
151             }
152         }
153     }
154 
invokeListeners(@onNull Key<T> key)155     private <T> void invokeListeners(@NonNull Key<T> key) {
156         synchronized (mLock) {
157             if (!mSettings.containsKey(key.key)) return;
158             Object newValue = mSettings.get(key.key);
159             Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
160             if (listeners == null || listeners.isEmpty()) return;
161             for (Map.Entry<OnSettingsChangedListener, Handler> listener
162                     : listeners.entrySet()) {
163                 // Trigger the callback in the appropriate handler.
164                 listener.getValue().post(() ->
165                         listener.getKey().onSettingsChanged(key, newValue));
166             }
167         }
168     }
169 
170     /**
171      * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
172      */
triggerSaveToStoreAndInvokeAllListeners()173     private void triggerSaveToStoreAndInvokeAllListeners() {
174         mHandler.post(() -> {
175             mHasNewDataToSerialize = true;
176             mWifiConfigManager.saveToStore(true);
177 
178             invokeAllListeners();
179         });
180     }
181 
182     /**
183      * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
184      */
triggerSaveToStoreAndInvokeListeners(@onNull Key<T> key)185     private <T> void triggerSaveToStoreAndInvokeListeners(@NonNull Key<T> key) {
186         mHandler.post(() -> {
187             mHasNewDataToSerialize = true;
188             mWifiConfigManager.saveToStore(true);
189 
190             invokeListeners(key);
191         });
192     }
193 
194     /**
195      * Performs a one time migration from Settings.Global values to settings store. Only
196      * performed one time if the settings store is empty.
197      */
migrateFromSettingsIfNeeded()198     private void migrateFromSettingsIfNeeded() {
199         if (!mSettings.isEmpty()) return; // already migrated.
200 
201         mCachedMigrationData = mSettingsMigrationDataHolder.retrieveData();
202         if (mCachedMigrationData == null) {
203             Log.e(TAG, "No settings data to migrate");
204             return;
205         }
206         Log.i(TAG, "Migrating data out of settings to shared preferences");
207 
208         mSettings.put(WIFI_P2P_DEVICE_NAME.key,
209                 mCachedMigrationData.getP2pDeviceName());
210         mSettings.put(WIFI_P2P_PENDING_FACTORY_RESET.key,
211                 mCachedMigrationData.isP2pFactoryResetPending());
212         mSettings.put(WIFI_SCAN_ALWAYS_AVAILABLE.key,
213                 mCachedMigrationData.isScanAlwaysAvailable());
214         mSettings.put(WIFI_SCAN_THROTTLE_ENABLED.key,
215                 mCachedMigrationData.isScanThrottleEnabled());
216         mSettings.put(WIFI_VERBOSE_LOGGING_ENABLED.key,
217                 mCachedMigrationData.isVerboseLoggingEnabled());
218         triggerSaveToStoreAndInvokeAllListeners();
219     }
220 
221     /**
222      * Store a value to the stored settings.
223      *
224      * @param key One of the settings keys.
225      * @param value Value to be stored.
226      */
put(@onNull Key<T> key, @Nullable T value)227     public <T> void put(@NonNull Key<T> key, @Nullable T value) {
228         synchronized (mLock) {
229             mSettings.put(key.key, value);
230         }
231         triggerSaveToStoreAndInvokeListeners(key);
232     }
233 
234     /**
235      * Retrieve a value from the stored settings.
236      *
237      * @param key One of the settings keys.
238      * @return value stored in settings, defValue if the key does not exist.
239      */
get(@onNull Key<T> key)240     public @Nullable <T> T get(@NonNull Key<T> key) {
241         synchronized (mLock) {
242             return (T) mSettings.getOrDefault(key.key, key.defaultValue);
243         }
244     }
245 
246     /**
247      * Register for settings change listener.
248      *
249      * @param key One of the settings keys.
250      * @param listener Listener to be registered.
251      * @param handler Handler to post the listener
252      */
registerChangeListener(@onNull Key<T> key, @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler)253     public <T> void registerChangeListener(@NonNull Key<T> key,
254             @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler) {
255         synchronized (mLock) {
256             mListeners.computeIfAbsent(
257                     key.key, ignore -> new HashMap<>()).put(listener, handler);
258         }
259     }
260 
261     /**
262      * Unregister for settings change listener.
263      *
264      * @param key One of the settings keys.
265      * @param listener Listener to be unregistered.
266      */
unregisterChangeListener(@onNull Key<T> key, @NonNull OnSettingsChangedListener<T> listener)267     public <T> void unregisterChangeListener(@NonNull Key<T> key,
268             @NonNull OnSettingsChangedListener<T> listener) {
269         synchronized (mLock) {
270             Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
271             if (listeners == null || listeners.isEmpty()) {
272                 Log.e(TAG, "No listeners for " + key);
273                 return;
274             }
275             if (listeners.remove(listener) == null) {
276                 Log.e(TAG, "Unknown listener for " + key);
277             }
278         }
279     }
280 
281     /**
282      * Dump output for debugging.
283      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)284     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
285         pw.println();
286         pw.println("Dump of " + TAG);
287         pw.println("Settings:");
288         for (Map.Entry<String, Object> entry : mSettings.entrySet()) {
289             pw.print(entry.getKey());
290             pw.print("=");
291             pw.println(entry.getValue());
292         }
293         if (mCachedMigrationData == null) return;
294         pw.println("Migration data:");
295         pw.print(WIFI_P2P_DEVICE_NAME.key);
296         pw.print("=");
297         pw.println(mCachedMigrationData.getP2pDeviceName());
298         pw.print(WIFI_P2P_PENDING_FACTORY_RESET.key);
299         pw.print("=");
300         pw.println(mCachedMigrationData.isP2pFactoryResetPending());
301         pw.print(WIFI_SCAN_ALWAYS_AVAILABLE.key);
302         pw.print("=");
303         pw.println(mCachedMigrationData.isScanAlwaysAvailable());
304         pw.print(WIFI_SCAN_THROTTLE_ENABLED.key);
305         pw.print("=");
306         pw.println(mCachedMigrationData.isScanThrottleEnabled());
307         pw.print(WIFI_VERBOSE_LOGGING_ENABLED.key);
308         pw.print("=");
309         pw.println(mCachedMigrationData.isVerboseLoggingEnabled());
310         pw.println();
311     }
312 
313     /**
314      * Base class to store string key and its default value.
315      * @param <T> Type of the value.
316      */
317     public static class Key<T> {
318         public final String key;
319         public final T defaultValue;
320 
Key(@onNull String key, T defaultValue)321         private Key(@NonNull String key, T defaultValue) {
322             this.key = key;
323             this.defaultValue = defaultValue;
324             sKeys.add(this);
325         }
326 
327         @Override
toString()328         public String toString() {
329             return "[Key " + key + ", DefaultValue: " + defaultValue + "]";
330         }
331     }
332 
333     /**
334      * Store data for persisting the settings data to config store.
335      */
336     private class StoreData implements WifiConfigStore.StoreData {
337         private static final String XML_TAG_SECTION_HEADER = "Settings";
338         private static final String XML_TAG_VALUES = "Values";
339 
340         @Override
serializeData(XmlSerializer out, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)341         public void serializeData(XmlSerializer out,
342                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
343                 throws XmlPullParserException, IOException {
344             synchronized (mLock) {
345                 XmlUtil.writeNextValue(out, XML_TAG_VALUES, mSettings);
346             }
347         }
348 
349         @Override
deserializeData(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)350         public void deserializeData(XmlPullParser in, int outerTagDepth,
351                 @WifiConfigStore.Version int version,
352                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
353                 throws XmlPullParserException, IOException {
354             if (in == null) {
355                 // Empty read triggers the migration since it indicates that there is no settings
356                 // data stored in the settings store.
357                 migrateFromSettingsIfNeeded();
358                 return;
359             }
360             Map<String, Object> values = null;
361             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
362                 String[] valueName = new String[1];
363                 Object value = XmlUtil.readCurrentValue(in, valueName);
364                 if (TextUtils.isEmpty(valueName[0])) {
365                     throw new XmlPullParserException("Missing value name");
366                 }
367                 switch (valueName[0]) {
368                     case XML_TAG_VALUES:
369                         values = (Map) value;
370                         break;
371                     default:
372                         Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
373                                 + valueName[0]);
374                         break;
375                 }
376             }
377             if (values != null) {
378                 synchronized (mLock) {
379                     mSettings.putAll(values);
380                     // Invoke all the registered listeners.
381                     invokeAllListeners();
382                 }
383             }
384         }
385 
386         @Override
resetData()387         public void resetData() {
388             synchronized (mLock) {
389                 mSettings.clear();
390             }
391         }
392 
393         @Override
hasNewDataToSerialize()394         public boolean hasNewDataToSerialize() {
395             return mHasNewDataToSerialize;
396         }
397 
398         @Override
getName()399         public String getName() {
400             return XML_TAG_SECTION_HEADER;
401         }
402 
403         @Override
getStoreFileId()404         public @WifiConfigStore.StoreFileId int getStoreFileId() {
405             // Shared general store.
406             return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
407         }
408     }
409 }
410