blob: 444359b33c0ef42fa0cf2c9302421ac383a3a176 [file] [log] [blame]
Roshan Piusb790f672020-02-14 07:06:13 -08001/*
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
17package com.android.server.wifi;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
Roshan Piusb790f672020-02-14 07:06:13 -080021import android.content.Context;
Roshan Pius3225f112020-02-24 09:12:04 -080022import android.net.wifi.WifiMigration;
Roshan Piusb790f672020-02-14 07:06:13 -080023import android.os.Handler;
24import android.text.TextUtils;
25import android.util.Log;
26
27import com.android.internal.annotations.GuardedBy;
Roshan Piusb0ed8e22020-03-19 09:08:16 -070028import com.android.server.wifi.util.SettingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -080029import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
30import com.android.server.wifi.util.XmlUtil;
31
Roshan Piusb790f672020-02-14 07:06:13 -080032import org.xmlpull.v1.XmlPullParser;
33import org.xmlpull.v1.XmlPullParserException;
34import org.xmlpull.v1.XmlSerializer;
35
Roshan Piusda0d1992020-03-04 08:34:42 -080036import java.io.FileDescriptor;
Roshan Piusb790f672020-02-14 07:06:13 -080037import java.io.IOException;
Roshan Piusda0d1992020-03-04 08:34:42 -080038import java.io.PrintWriter;
Roshan Pius67ee4292020-02-25 09:51:02 -080039import java.util.ArrayList;
Roshan Piusb790f672020-02-14 07:06:13 -080040import java.util.HashMap;
41import 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 */
47public class WifiSettingsConfigStore {
48 private static final String TAG = "WifiSettingsConfigStore";
49
Roshan Pius67ee4292020-02-25 09:51:02 -080050 // List of all allowed keys.
51 private static final ArrayList<Key> sKeys = new ArrayList<>();
52
Roshan Piusb790f672020-02-14 07:06:13 -080053 /******** Wifi shared pref keys ***************/
Roshan Pius0412d2d2020-02-10 13:26:42 -080054 /**
55 * Indicate whether factory reset request is pending.
56 */
Roshan Pius67ee4292020-02-25 09:51:02 -080057 public static final Key<Boolean> WIFI_P2P_PENDING_FACTORY_RESET =
58 new Key<>("wifi_p2p_pending_factory_reset", false);
Roshan Pius0412d2d2020-02-10 13:26:42 -080059
60 /**
61 * Allow scans to be enabled even wifi is turned off.
62 */
Roshan Pius67ee4292020-02-25 09:51:02 -080063 public static final Key<Boolean> WIFI_SCAN_ALWAYS_AVAILABLE =
64 new Key<>("wifi_scan_always_enabled", false);
Roshan Pius0412d2d2020-02-10 13:26:42 -080065
66 /**
67 * Whether wifi scan throttle is enabled or not.
68 */
Roshan Pius67ee4292020-02-25 09:51:02 -080069 public static final Key<Boolean> WIFI_SCAN_THROTTLE_ENABLED =
70 new Key<>("wifi_scan_throttle_enabled", true);
Roshan Pius0412d2d2020-02-10 13:26:42 -080071
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 */
Roshan Pius67ee4292020-02-25 09:51:02 -080076 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
Mingguang Xu066d4442021-01-28 14:07:17 -080085 /**
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
Roshan Pius1ec76102021-02-11 12:06:57 -080091 /**
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
Roshan Piusb790f672020-02-14 07:06:13 -080097 /******** Wifi shared pref keys ***************/
98
99 private final Context mContext;
100 private final Handler mHandler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700101 private final SettingsMigrationDataHolder mSettingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -0800102 private final WifiConfigManager mWifiConfigManager;
103
104 private final Object mLock = new Object();
105 @GuardedBy("mLock")
106 private final Map<String, Object> mSettings = new HashMap<>();
107 @GuardedBy("mLock")
108 private final Map<String, Map<OnSettingsChangedListener, Handler>> mListeners =
109 new HashMap<>();
Roshan Piusda0d1992020-03-04 08:34:42 -0800110 private WifiMigration.SettingsMigrationData mCachedMigrationData = null;
Roshan Piusb790f672020-02-14 07:06:13 -0800111
112 private boolean mHasNewDataToSerialize = false;
113
114 /**
115 * Interface for a settings change listener.
Roshan Pius67ee4292020-02-25 09:51:02 -0800116 * @param <T> Type of the value.
Roshan Piusb790f672020-02-14 07:06:13 -0800117 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800118 public interface OnSettingsChangedListener<T> {
Roshan Piusb790f672020-02-14 07:06:13 -0800119 /**
120 * Invoked when a particular key settings changes.
121 *
122 * @param key Key that was changed.
123 * @param newValue New value that was assigned to the key.
124 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800125 void onSettingsChanged(@NonNull Key<T> key, @Nullable T newValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800126 }
127
128 public WifiSettingsConfigStore(@NonNull Context context, @NonNull Handler handler,
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700129 @NonNull SettingsMigrationDataHolder settingsMigrationDataHolder,
Roshan Piusb790f672020-02-14 07:06:13 -0800130 @NonNull WifiConfigManager wifiConfigManager,
131 @NonNull WifiConfigStore wifiConfigStore) {
132 mContext = context;
133 mHandler = handler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700134 mSettingsMigrationDataHolder = settingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -0800135 mWifiConfigManager = wifiConfigManager;
136
137 // Register our data store.
138 wifiConfigStore.registerStoreData(new StoreData());
139 }
140
141 private void invokeAllListeners() {
142 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800143 for (Key key : sKeys) {
Roshan Piusb790f672020-02-14 07:06:13 -0800144 invokeListeners(key);
145 }
146 }
147 }
148
Roshan Pius67ee4292020-02-25 09:51:02 -0800149 private <T> void invokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800150 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800151 if (!mSettings.containsKey(key.key)) return;
152 Object newValue = mSettings.get(key.key);
153 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800154 if (listeners == null || listeners.isEmpty()) return;
155 for (Map.Entry<OnSettingsChangedListener, Handler> listener
156 : listeners.entrySet()) {
157 // Trigger the callback in the appropriate handler.
Roshan Pius67ee4292020-02-25 09:51:02 -0800158 listener.getValue().post(() ->
159 listener.getKey().onSettingsChanged(key, newValue));
Roshan Piusb790f672020-02-14 07:06:13 -0800160 }
161 }
162 }
163
164 /**
165 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
166 */
Roshan Pius0412d2d2020-02-10 13:26:42 -0800167 private void triggerSaveToStoreAndInvokeAllListeners() {
168 mHandler.post(() -> {
169 mHasNewDataToSerialize = true;
170 mWifiConfigManager.saveToStore(true);
171
172 invokeAllListeners();
173 });
174 }
175
176 /**
177 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
178 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800179 private <T> void triggerSaveToStoreAndInvokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800180 mHandler.post(() -> {
181 mHasNewDataToSerialize = true;
182 mWifiConfigManager.saveToStore(true);
183
184 invokeListeners(key);
185 });
186 }
187
188 /**
Roshan Pius0412d2d2020-02-10 13:26:42 -0800189 * Performs a one time migration from Settings.Global values to settings store. Only
190 * performed one time if the settings store is empty.
191 */
192 private void migrateFromSettingsIfNeeded() {
193 if (!mSettings.isEmpty()) return; // already migrated.
194
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700195 mCachedMigrationData = mSettingsMigrationDataHolder.retrieveData();
Roshan Piusda0d1992020-03-04 08:34:42 -0800196 if (mCachedMigrationData == null) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800197 Log.e(TAG, "No settings data to migrate");
Roshan Pius0412d2d2020-02-10 13:26:42 -0800198 return;
199 }
200 Log.i(TAG, "Migrating data out of settings to shared preferences");
201
Roshan Pius67ee4292020-02-25 09:51:02 -0800202 mSettings.put(WIFI_P2P_DEVICE_NAME.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800203 mCachedMigrationData.getP2pDeviceName());
Roshan Pius67ee4292020-02-25 09:51:02 -0800204 mSettings.put(WIFI_P2P_PENDING_FACTORY_RESET.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800205 mCachedMigrationData.isP2pFactoryResetPending());
Roshan Pius67ee4292020-02-25 09:51:02 -0800206 mSettings.put(WIFI_SCAN_ALWAYS_AVAILABLE.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800207 mCachedMigrationData.isScanAlwaysAvailable());
Roshan Pius67ee4292020-02-25 09:51:02 -0800208 mSettings.put(WIFI_SCAN_THROTTLE_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800209 mCachedMigrationData.isScanThrottleEnabled());
Roshan Pius67ee4292020-02-25 09:51:02 -0800210 mSettings.put(WIFI_VERBOSE_LOGGING_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800211 mCachedMigrationData.isVerboseLoggingEnabled());
Roshan Pius0412d2d2020-02-10 13:26:42 -0800212 triggerSaveToStoreAndInvokeAllListeners();
213 }
214
215 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800216 * Store a value to the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800217 *
218 * @param key One of the settings keys.
219 * @param value Value to be stored.
220 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800221 public <T> void put(@NonNull Key<T> key, @Nullable T value) {
Roshan Piusb790f672020-02-14 07:06:13 -0800222 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800223 mSettings.put(key.key, value);
Roshan Piusb790f672020-02-14 07:06:13 -0800224 }
225 triggerSaveToStoreAndInvokeListeners(key);
226 }
227
228 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800229 * Retrieve a value from the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800230 *
231 * @param key One of the settings keys.
Roshan Piusb790f672020-02-14 07:06:13 -0800232 * @return value stored in settings, defValue if the key does not exist.
233 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800234 public @Nullable <T> T get(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800235 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800236 return (T) mSettings.getOrDefault(key.key, key.defaultValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800237 }
238 }
239
240 /**
241 * Register for settings change listener.
242 *
243 * @param key One of the settings keys.
244 * @param listener Listener to be registered.
245 * @param handler Handler to post the listener
246 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800247 public <T> void registerChangeListener(@NonNull Key<T> key,
248 @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler) {
Roshan Piusb790f672020-02-14 07:06:13 -0800249 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800250 mListeners.computeIfAbsent(
251 key.key, ignore -> new HashMap<>()).put(listener, handler);
Roshan Piusb790f672020-02-14 07:06:13 -0800252 }
253 }
254
255 /**
256 * Unregister for settings change listener.
257 *
258 * @param key One of the settings keys.
259 * @param listener Listener to be unregistered.
260 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800261 public <T> void unregisterChangeListener(@NonNull Key<T> key,
262 @NonNull OnSettingsChangedListener<T> listener) {
Roshan Piusb790f672020-02-14 07:06:13 -0800263 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800264 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800265 if (listeners == null || listeners.isEmpty()) {
266 Log.e(TAG, "No listeners for " + key);
267 return;
268 }
269 if (listeners.remove(listener) == null) {
270 Log.e(TAG, "Unknown listener for " + key);
271 }
272 }
273 }
274
275 /**
Roshan Piusda0d1992020-03-04 08:34:42 -0800276 * Dump output for debugging.
277 */
278 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
279 pw.println();
280 pw.println("Dump of " + TAG);
281 pw.println("Settings:");
282 for (Map.Entry<String, Object> entry : mSettings.entrySet()) {
283 pw.print(entry.getKey());
284 pw.print("=");
285 pw.println(entry.getValue());
286 }
287 if (mCachedMigrationData == null) return;
288 pw.println("Migration data:");
289 pw.print(WIFI_P2P_DEVICE_NAME.key);
290 pw.print("=");
291 pw.println(mCachedMigrationData.getP2pDeviceName());
292 pw.print(WIFI_P2P_PENDING_FACTORY_RESET.key);
293 pw.print("=");
294 pw.println(mCachedMigrationData.isP2pFactoryResetPending());
295 pw.print(WIFI_SCAN_ALWAYS_AVAILABLE.key);
296 pw.print("=");
297 pw.println(mCachedMigrationData.isScanAlwaysAvailable());
298 pw.print(WIFI_SCAN_THROTTLE_ENABLED.key);
299 pw.print("=");
300 pw.println(mCachedMigrationData.isScanThrottleEnabled());
301 pw.print(WIFI_VERBOSE_LOGGING_ENABLED.key);
302 pw.print("=");
303 pw.println(mCachedMigrationData.isVerboseLoggingEnabled());
304 pw.println();
305 }
306
307 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800308 * Base class to store string key and its default value.
309 * @param <T> Type of the value.
310 */
311 public static class Key<T> {
312 public final String key;
313 public final T defaultValue;
314
315 private Key(@NonNull String key, T defaultValue) {
316 this.key = key;
317 this.defaultValue = defaultValue;
318 sKeys.add(this);
319 }
320
321 @Override
322 public String toString() {
323 return "[Key " + key + ", DefaultValue: " + defaultValue + "]";
324 }
325 }
326
327 /**
Roshan Piusb790f672020-02-14 07:06:13 -0800328 * Store data for persisting the settings data to config store.
329 */
330 private class StoreData implements WifiConfigStore.StoreData {
331 private static final String XML_TAG_SECTION_HEADER = "Settings";
332 private static final String XML_TAG_VALUES = "Values";
333
334 @Override
335 public void serializeData(XmlSerializer out,
336 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
337 throws XmlPullParserException, IOException {
338 synchronized (mLock) {
339 XmlUtil.writeNextValue(out, XML_TAG_VALUES, mSettings);
340 }
341 }
342
343 @Override
344 public void deserializeData(XmlPullParser in, int outerTagDepth,
345 @WifiConfigStore.Version int version,
Roshan Piusfdf57e62020-03-16 13:33:16 -0700346 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
Roshan Piusb790f672020-02-14 07:06:13 -0800347 throws XmlPullParserException, IOException {
Roshan Piusb790f672020-02-14 07:06:13 -0800348 if (in == null) {
Roshan Pius0412d2d2020-02-10 13:26:42 -0800349 // Empty read triggers the migration since it indicates that there is no settings
350 // data stored in the settings store.
351 migrateFromSettingsIfNeeded();
Roshan Piusb790f672020-02-14 07:06:13 -0800352 return;
353 }
354 Map<String, Object> values = null;
355 while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
356 String[] valueName = new String[1];
357 Object value = XmlUtil.readCurrentValue(in, valueName);
358 if (TextUtils.isEmpty(valueName[0])) {
359 throw new XmlPullParserException("Missing value name");
360 }
361 switch (valueName[0]) {
362 case XML_TAG_VALUES:
363 values = (Map) value;
364 break;
365 default:
366 Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
367 + valueName[0]);
368 break;
369 }
370 }
371 if (values != null) {
372 synchronized (mLock) {
373 mSettings.putAll(values);
374 // Invoke all the registered listeners.
375 invokeAllListeners();
376 }
377 }
378 }
379
380 @Override
381 public void resetData() {
382 synchronized (mLock) {
383 mSettings.clear();
384 }
385 }
386
387 @Override
388 public boolean hasNewDataToSerialize() {
389 return mHasNewDataToSerialize;
390 }
391
392 @Override
393 public String getName() {
394 return XML_TAG_SECTION_HEADER;
395 }
396
397 @Override
398 public @WifiConfigStore.StoreFileId int getStoreFileId() {
399 // Shared general store.
Roshan Pius0412d2d2020-02-10 13:26:42 -0800400 return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
Roshan Piusb790f672020-02-14 07:06:13 -0800401 }
402 }
403}