blob: c707b30f09a3de0705beb30f868e312285fe6837 [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 Piusb790f672020-02-14 07:06:13 -080091 /******** Wifi shared pref keys ***************/
92
93 private final Context mContext;
94 private final Handler mHandler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -070095 private final SettingsMigrationDataHolder mSettingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -080096 private final WifiConfigManager mWifiConfigManager;
97
98 private final Object mLock = new Object();
99 @GuardedBy("mLock")
100 private final Map<String, Object> mSettings = new HashMap<>();
101 @GuardedBy("mLock")
102 private final Map<String, Map<OnSettingsChangedListener, Handler>> mListeners =
103 new HashMap<>();
Roshan Piusda0d1992020-03-04 08:34:42 -0800104 private WifiMigration.SettingsMigrationData mCachedMigrationData = null;
Roshan Piusb790f672020-02-14 07:06:13 -0800105
106 private boolean mHasNewDataToSerialize = false;
107
108 /**
109 * Interface for a settings change listener.
Roshan Pius67ee4292020-02-25 09:51:02 -0800110 * @param <T> Type of the value.
Roshan Piusb790f672020-02-14 07:06:13 -0800111 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800112 public interface OnSettingsChangedListener<T> {
Roshan Piusb790f672020-02-14 07:06:13 -0800113 /**
114 * Invoked when a particular key settings changes.
115 *
116 * @param key Key that was changed.
117 * @param newValue New value that was assigned to the key.
118 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800119 void onSettingsChanged(@NonNull Key<T> key, @Nullable T newValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800120 }
121
122 public WifiSettingsConfigStore(@NonNull Context context, @NonNull Handler handler,
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700123 @NonNull SettingsMigrationDataHolder settingsMigrationDataHolder,
Roshan Piusb790f672020-02-14 07:06:13 -0800124 @NonNull WifiConfigManager wifiConfigManager,
125 @NonNull WifiConfigStore wifiConfigStore) {
126 mContext = context;
127 mHandler = handler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700128 mSettingsMigrationDataHolder = settingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -0800129 mWifiConfigManager = wifiConfigManager;
130
131 // Register our data store.
132 wifiConfigStore.registerStoreData(new StoreData());
133 }
134
135 private void invokeAllListeners() {
136 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800137 for (Key key : sKeys) {
Roshan Piusb790f672020-02-14 07:06:13 -0800138 invokeListeners(key);
139 }
140 }
141 }
142
Roshan Pius67ee4292020-02-25 09:51:02 -0800143 private <T> void invokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800144 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800145 if (!mSettings.containsKey(key.key)) return;
146 Object newValue = mSettings.get(key.key);
147 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800148 if (listeners == null || listeners.isEmpty()) return;
149 for (Map.Entry<OnSettingsChangedListener, Handler> listener
150 : listeners.entrySet()) {
151 // Trigger the callback in the appropriate handler.
Roshan Pius67ee4292020-02-25 09:51:02 -0800152 listener.getValue().post(() ->
153 listener.getKey().onSettingsChanged(key, newValue));
Roshan Piusb790f672020-02-14 07:06:13 -0800154 }
155 }
156 }
157
158 /**
159 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
160 */
Roshan Pius0412d2d2020-02-10 13:26:42 -0800161 private void triggerSaveToStoreAndInvokeAllListeners() {
162 mHandler.post(() -> {
163 mHasNewDataToSerialize = true;
164 mWifiConfigManager.saveToStore(true);
165
166 invokeAllListeners();
167 });
168 }
169
170 /**
171 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
172 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800173 private <T> void triggerSaveToStoreAndInvokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800174 mHandler.post(() -> {
175 mHasNewDataToSerialize = true;
176 mWifiConfigManager.saveToStore(true);
177
178 invokeListeners(key);
179 });
180 }
181
182 /**
Roshan Pius0412d2d2020-02-10 13:26:42 -0800183 * Performs a one time migration from Settings.Global values to settings store. Only
184 * performed one time if the settings store is empty.
185 */
186 private void migrateFromSettingsIfNeeded() {
187 if (!mSettings.isEmpty()) return; // already migrated.
188
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700189 mCachedMigrationData = mSettingsMigrationDataHolder.retrieveData();
Roshan Piusda0d1992020-03-04 08:34:42 -0800190 if (mCachedMigrationData == null) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800191 Log.e(TAG, "No settings data to migrate");
Roshan Pius0412d2d2020-02-10 13:26:42 -0800192 return;
193 }
194 Log.i(TAG, "Migrating data out of settings to shared preferences");
195
Roshan Pius67ee4292020-02-25 09:51:02 -0800196 mSettings.put(WIFI_P2P_DEVICE_NAME.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800197 mCachedMigrationData.getP2pDeviceName());
Roshan Pius67ee4292020-02-25 09:51:02 -0800198 mSettings.put(WIFI_P2P_PENDING_FACTORY_RESET.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800199 mCachedMigrationData.isP2pFactoryResetPending());
Roshan Pius67ee4292020-02-25 09:51:02 -0800200 mSettings.put(WIFI_SCAN_ALWAYS_AVAILABLE.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800201 mCachedMigrationData.isScanAlwaysAvailable());
Roshan Pius67ee4292020-02-25 09:51:02 -0800202 mSettings.put(WIFI_SCAN_THROTTLE_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800203 mCachedMigrationData.isScanThrottleEnabled());
Roshan Pius67ee4292020-02-25 09:51:02 -0800204 mSettings.put(WIFI_VERBOSE_LOGGING_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800205 mCachedMigrationData.isVerboseLoggingEnabled());
Roshan Pius0412d2d2020-02-10 13:26:42 -0800206 triggerSaveToStoreAndInvokeAllListeners();
207 }
208
209 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800210 * Store a value to the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800211 *
212 * @param key One of the settings keys.
213 * @param value Value to be stored.
214 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800215 public <T> void put(@NonNull Key<T> key, @Nullable T value) {
Roshan Piusb790f672020-02-14 07:06:13 -0800216 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800217 mSettings.put(key.key, value);
Roshan Piusb790f672020-02-14 07:06:13 -0800218 }
219 triggerSaveToStoreAndInvokeListeners(key);
220 }
221
222 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800223 * Retrieve a value from the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800224 *
225 * @param key One of the settings keys.
Roshan Piusb790f672020-02-14 07:06:13 -0800226 * @return value stored in settings, defValue if the key does not exist.
227 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800228 public @Nullable <T> T get(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800229 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800230 return (T) mSettings.getOrDefault(key.key, key.defaultValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800231 }
232 }
233
234 /**
235 * Register for settings change listener.
236 *
237 * @param key One of the settings keys.
238 * @param listener Listener to be registered.
239 * @param handler Handler to post the listener
240 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800241 public <T> void registerChangeListener(@NonNull Key<T> key,
242 @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler) {
Roshan Piusb790f672020-02-14 07:06:13 -0800243 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800244 mListeners.computeIfAbsent(
245 key.key, ignore -> new HashMap<>()).put(listener, handler);
Roshan Piusb790f672020-02-14 07:06:13 -0800246 }
247 }
248
249 /**
250 * Unregister for settings change listener.
251 *
252 * @param key One of the settings keys.
253 * @param listener Listener to be unregistered.
254 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800255 public <T> void unregisterChangeListener(@NonNull Key<T> key,
256 @NonNull OnSettingsChangedListener<T> listener) {
Roshan Piusb790f672020-02-14 07:06:13 -0800257 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800258 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800259 if (listeners == null || listeners.isEmpty()) {
260 Log.e(TAG, "No listeners for " + key);
261 return;
262 }
263 if (listeners.remove(listener) == null) {
264 Log.e(TAG, "Unknown listener for " + key);
265 }
266 }
267 }
268
269 /**
Roshan Piusda0d1992020-03-04 08:34:42 -0800270 * Dump output for debugging.
271 */
272 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
273 pw.println();
274 pw.println("Dump of " + TAG);
275 pw.println("Settings:");
276 for (Map.Entry<String, Object> entry : mSettings.entrySet()) {
277 pw.print(entry.getKey());
278 pw.print("=");
279 pw.println(entry.getValue());
280 }
281 if (mCachedMigrationData == null) return;
282 pw.println("Migration data:");
283 pw.print(WIFI_P2P_DEVICE_NAME.key);
284 pw.print("=");
285 pw.println(mCachedMigrationData.getP2pDeviceName());
286 pw.print(WIFI_P2P_PENDING_FACTORY_RESET.key);
287 pw.print("=");
288 pw.println(mCachedMigrationData.isP2pFactoryResetPending());
289 pw.print(WIFI_SCAN_ALWAYS_AVAILABLE.key);
290 pw.print("=");
291 pw.println(mCachedMigrationData.isScanAlwaysAvailable());
292 pw.print(WIFI_SCAN_THROTTLE_ENABLED.key);
293 pw.print("=");
294 pw.println(mCachedMigrationData.isScanThrottleEnabled());
295 pw.print(WIFI_VERBOSE_LOGGING_ENABLED.key);
296 pw.print("=");
297 pw.println(mCachedMigrationData.isVerboseLoggingEnabled());
298 pw.println();
299 }
300
301 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800302 * Base class to store string key and its default value.
303 * @param <T> Type of the value.
304 */
305 public static class Key<T> {
306 public final String key;
307 public final T defaultValue;
308
309 private Key(@NonNull String key, T defaultValue) {
310 this.key = key;
311 this.defaultValue = defaultValue;
312 sKeys.add(this);
313 }
314
315 @Override
316 public String toString() {
317 return "[Key " + key + ", DefaultValue: " + defaultValue + "]";
318 }
319 }
320
321 /**
Roshan Piusb790f672020-02-14 07:06:13 -0800322 * Store data for persisting the settings data to config store.
323 */
324 private class StoreData implements WifiConfigStore.StoreData {
325 private static final String XML_TAG_SECTION_HEADER = "Settings";
326 private static final String XML_TAG_VALUES = "Values";
327
328 @Override
329 public void serializeData(XmlSerializer out,
330 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
331 throws XmlPullParserException, IOException {
332 synchronized (mLock) {
333 XmlUtil.writeNextValue(out, XML_TAG_VALUES, mSettings);
334 }
335 }
336
337 @Override
338 public void deserializeData(XmlPullParser in, int outerTagDepth,
339 @WifiConfigStore.Version int version,
Roshan Piusfdf57e62020-03-16 13:33:16 -0700340 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
Roshan Piusb790f672020-02-14 07:06:13 -0800341 throws XmlPullParserException, IOException {
Roshan Piusb790f672020-02-14 07:06:13 -0800342 if (in == null) {
Roshan Pius0412d2d2020-02-10 13:26:42 -0800343 // Empty read triggers the migration since it indicates that there is no settings
344 // data stored in the settings store.
345 migrateFromSettingsIfNeeded();
Roshan Piusb790f672020-02-14 07:06:13 -0800346 return;
347 }
348 Map<String, Object> values = null;
349 while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
350 String[] valueName = new String[1];
351 Object value = XmlUtil.readCurrentValue(in, valueName);
352 if (TextUtils.isEmpty(valueName[0])) {
353 throw new XmlPullParserException("Missing value name");
354 }
355 switch (valueName[0]) {
356 case XML_TAG_VALUES:
357 values = (Map) value;
358 break;
359 default:
360 Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
361 + valueName[0]);
362 break;
363 }
364 }
365 if (values != null) {
366 synchronized (mLock) {
367 mSettings.putAll(values);
368 // Invoke all the registered listeners.
369 invokeAllListeners();
370 }
371 }
372 }
373
374 @Override
375 public void resetData() {
376 synchronized (mLock) {
377 mSettings.clear();
378 }
379 }
380
381 @Override
382 public boolean hasNewDataToSerialize() {
383 return mHasNewDataToSerialize;
384 }
385
386 @Override
387 public String getName() {
388 return XML_TAG_SECTION_HEADER;
389 }
390
391 @Override
392 public @WifiConfigStore.StoreFileId int getStoreFileId() {
393 // Shared general store.
Roshan Pius0412d2d2020-02-10 13:26:42 -0800394 return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
Roshan Piusb790f672020-02-14 07:06:13 -0800395 }
396 }
397}