blob: d77aa480377a55237ed4d43432cb7d63d9796fd0 [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
Roshan Piusb790f672020-02-14 07:06:13 -080085 /******** Wifi shared pref keys ***************/
86
87 private final Context mContext;
88 private final Handler mHandler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -070089 private final SettingsMigrationDataHolder mSettingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -080090 private final WifiConfigManager mWifiConfigManager;
91
92 private final Object mLock = new Object();
93 @GuardedBy("mLock")
94 private final Map<String, Object> mSettings = new HashMap<>();
95 @GuardedBy("mLock")
96 private final Map<String, Map<OnSettingsChangedListener, Handler>> mListeners =
97 new HashMap<>();
Roshan Piusda0d1992020-03-04 08:34:42 -080098 private WifiMigration.SettingsMigrationData mCachedMigrationData = null;
Roshan Piusb790f672020-02-14 07:06:13 -080099
100 private boolean mHasNewDataToSerialize = false;
101
102 /**
103 * Interface for a settings change listener.
Roshan Pius67ee4292020-02-25 09:51:02 -0800104 * @param <T> Type of the value.
Roshan Piusb790f672020-02-14 07:06:13 -0800105 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800106 public interface OnSettingsChangedListener<T> {
Roshan Piusb790f672020-02-14 07:06:13 -0800107 /**
108 * Invoked when a particular key settings changes.
109 *
110 * @param key Key that was changed.
111 * @param newValue New value that was assigned to the key.
112 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800113 void onSettingsChanged(@NonNull Key<T> key, @Nullable T newValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800114 }
115
116 public WifiSettingsConfigStore(@NonNull Context context, @NonNull Handler handler,
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700117 @NonNull SettingsMigrationDataHolder settingsMigrationDataHolder,
Roshan Piusb790f672020-02-14 07:06:13 -0800118 @NonNull WifiConfigManager wifiConfigManager,
119 @NonNull WifiConfigStore wifiConfigStore) {
120 mContext = context;
121 mHandler = handler;
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700122 mSettingsMigrationDataHolder = settingsMigrationDataHolder;
Roshan Piusb790f672020-02-14 07:06:13 -0800123 mWifiConfigManager = wifiConfigManager;
124
125 // Register our data store.
126 wifiConfigStore.registerStoreData(new StoreData());
127 }
128
129 private void invokeAllListeners() {
130 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800131 for (Key key : sKeys) {
Roshan Piusb790f672020-02-14 07:06:13 -0800132 invokeListeners(key);
133 }
134 }
135 }
136
Roshan Pius67ee4292020-02-25 09:51:02 -0800137 private <T> void invokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800138 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800139 if (!mSettings.containsKey(key.key)) return;
140 Object newValue = mSettings.get(key.key);
141 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800142 if (listeners == null || listeners.isEmpty()) return;
143 for (Map.Entry<OnSettingsChangedListener, Handler> listener
144 : listeners.entrySet()) {
145 // Trigger the callback in the appropriate handler.
Roshan Pius67ee4292020-02-25 09:51:02 -0800146 listener.getValue().post(() ->
147 listener.getKey().onSettingsChanged(key, newValue));
Roshan Piusb790f672020-02-14 07:06:13 -0800148 }
149 }
150 }
151
152 /**
153 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
154 */
Roshan Pius0412d2d2020-02-10 13:26:42 -0800155 private void triggerSaveToStoreAndInvokeAllListeners() {
156 mHandler.post(() -> {
157 mHasNewDataToSerialize = true;
158 mWifiConfigManager.saveToStore(true);
159
160 invokeAllListeners();
161 });
162 }
163
164 /**
165 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
166 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800167 private <T> void triggerSaveToStoreAndInvokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800168 mHandler.post(() -> {
169 mHasNewDataToSerialize = true;
170 mWifiConfigManager.saveToStore(true);
171
172 invokeListeners(key);
173 });
174 }
175
176 /**
Roshan Pius0412d2d2020-02-10 13:26:42 -0800177 * Performs a one time migration from Settings.Global values to settings store. Only
178 * performed one time if the settings store is empty.
179 */
180 private void migrateFromSettingsIfNeeded() {
181 if (!mSettings.isEmpty()) return; // already migrated.
182
Roshan Piusb0ed8e22020-03-19 09:08:16 -0700183 mCachedMigrationData = mSettingsMigrationDataHolder.retrieveData();
Roshan Piusda0d1992020-03-04 08:34:42 -0800184 if (mCachedMigrationData == null) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800185 Log.e(TAG, "No settings data to migrate");
Roshan Pius0412d2d2020-02-10 13:26:42 -0800186 return;
187 }
188 Log.i(TAG, "Migrating data out of settings to shared preferences");
189
Roshan Pius67ee4292020-02-25 09:51:02 -0800190 mSettings.put(WIFI_P2P_DEVICE_NAME.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800191 mCachedMigrationData.getP2pDeviceName());
Roshan Pius67ee4292020-02-25 09:51:02 -0800192 mSettings.put(WIFI_P2P_PENDING_FACTORY_RESET.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800193 mCachedMigrationData.isP2pFactoryResetPending());
Roshan Pius67ee4292020-02-25 09:51:02 -0800194 mSettings.put(WIFI_SCAN_ALWAYS_AVAILABLE.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800195 mCachedMigrationData.isScanAlwaysAvailable());
Roshan Pius67ee4292020-02-25 09:51:02 -0800196 mSettings.put(WIFI_SCAN_THROTTLE_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800197 mCachedMigrationData.isScanThrottleEnabled());
Roshan Pius67ee4292020-02-25 09:51:02 -0800198 mSettings.put(WIFI_VERBOSE_LOGGING_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800199 mCachedMigrationData.isVerboseLoggingEnabled());
Roshan Pius0412d2d2020-02-10 13:26:42 -0800200 triggerSaveToStoreAndInvokeAllListeners();
201 }
202
203 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800204 * Store a value to the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800205 *
206 * @param key One of the settings keys.
207 * @param value Value to be stored.
208 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800209 public <T> void put(@NonNull Key<T> key, @Nullable T value) {
Roshan Piusb790f672020-02-14 07:06:13 -0800210 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800211 mSettings.put(key.key, value);
Roshan Piusb790f672020-02-14 07:06:13 -0800212 }
213 triggerSaveToStoreAndInvokeListeners(key);
214 }
215
216 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800217 * Retrieve a value from the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800218 *
219 * @param key One of the settings keys.
Roshan Piusb790f672020-02-14 07:06:13 -0800220 * @return value stored in settings, defValue if the key does not exist.
221 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800222 public @Nullable <T> T get(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800223 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800224 return (T) mSettings.getOrDefault(key.key, key.defaultValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800225 }
226 }
227
228 /**
229 * Register for settings change listener.
230 *
231 * @param key One of the settings keys.
232 * @param listener Listener to be registered.
233 * @param handler Handler to post the listener
234 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800235 public <T> void registerChangeListener(@NonNull Key<T> key,
236 @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler) {
Roshan Piusb790f672020-02-14 07:06:13 -0800237 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800238 mListeners.computeIfAbsent(
239 key.key, ignore -> new HashMap<>()).put(listener, handler);
Roshan Piusb790f672020-02-14 07:06:13 -0800240 }
241 }
242
243 /**
244 * Unregister for settings change listener.
245 *
246 * @param key One of the settings keys.
247 * @param listener Listener to be unregistered.
248 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800249 public <T> void unregisterChangeListener(@NonNull Key<T> key,
250 @NonNull OnSettingsChangedListener<T> listener) {
Roshan Piusb790f672020-02-14 07:06:13 -0800251 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800252 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800253 if (listeners == null || listeners.isEmpty()) {
254 Log.e(TAG, "No listeners for " + key);
255 return;
256 }
257 if (listeners.remove(listener) == null) {
258 Log.e(TAG, "Unknown listener for " + key);
259 }
260 }
261 }
262
263 /**
Roshan Piusda0d1992020-03-04 08:34:42 -0800264 * Dump output for debugging.
265 */
266 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
267 pw.println();
268 pw.println("Dump of " + TAG);
269 pw.println("Settings:");
270 for (Map.Entry<String, Object> entry : mSettings.entrySet()) {
271 pw.print(entry.getKey());
272 pw.print("=");
273 pw.println(entry.getValue());
274 }
275 if (mCachedMigrationData == null) return;
276 pw.println("Migration data:");
277 pw.print(WIFI_P2P_DEVICE_NAME.key);
278 pw.print("=");
279 pw.println(mCachedMigrationData.getP2pDeviceName());
280 pw.print(WIFI_P2P_PENDING_FACTORY_RESET.key);
281 pw.print("=");
282 pw.println(mCachedMigrationData.isP2pFactoryResetPending());
283 pw.print(WIFI_SCAN_ALWAYS_AVAILABLE.key);
284 pw.print("=");
285 pw.println(mCachedMigrationData.isScanAlwaysAvailable());
286 pw.print(WIFI_SCAN_THROTTLE_ENABLED.key);
287 pw.print("=");
288 pw.println(mCachedMigrationData.isScanThrottleEnabled());
289 pw.print(WIFI_VERBOSE_LOGGING_ENABLED.key);
290 pw.print("=");
291 pw.println(mCachedMigrationData.isVerboseLoggingEnabled());
292 pw.println();
293 }
294
295 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800296 * Base class to store string key and its default value.
297 * @param <T> Type of the value.
298 */
299 public static class Key<T> {
300 public final String key;
301 public final T defaultValue;
302
303 private Key(@NonNull String key, T defaultValue) {
304 this.key = key;
305 this.defaultValue = defaultValue;
306 sKeys.add(this);
307 }
308
309 @Override
310 public String toString() {
311 return "[Key " + key + ", DefaultValue: " + defaultValue + "]";
312 }
313 }
314
315 /**
Roshan Piusb790f672020-02-14 07:06:13 -0800316 * Store data for persisting the settings data to config store.
317 */
318 private class StoreData implements WifiConfigStore.StoreData {
319 private static final String XML_TAG_SECTION_HEADER = "Settings";
320 private static final String XML_TAG_VALUES = "Values";
321
322 @Override
323 public void serializeData(XmlSerializer out,
324 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
325 throws XmlPullParserException, IOException {
326 synchronized (mLock) {
327 XmlUtil.writeNextValue(out, XML_TAG_VALUES, mSettings);
328 }
329 }
330
331 @Override
332 public void deserializeData(XmlPullParser in, int outerTagDepth,
333 @WifiConfigStore.Version int version,
Roshan Piusfdf57e62020-03-16 13:33:16 -0700334 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
Roshan Piusb790f672020-02-14 07:06:13 -0800335 throws XmlPullParserException, IOException {
Roshan Piusb790f672020-02-14 07:06:13 -0800336 if (in == null) {
Roshan Pius0412d2d2020-02-10 13:26:42 -0800337 // Empty read triggers the migration since it indicates that there is no settings
338 // data stored in the settings store.
339 migrateFromSettingsIfNeeded();
Roshan Piusb790f672020-02-14 07:06:13 -0800340 return;
341 }
342 Map<String, Object> values = null;
343 while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
344 String[] valueName = new String[1];
345 Object value = XmlUtil.readCurrentValue(in, valueName);
346 if (TextUtils.isEmpty(valueName[0])) {
347 throw new XmlPullParserException("Missing value name");
348 }
349 switch (valueName[0]) {
350 case XML_TAG_VALUES:
351 values = (Map) value;
352 break;
353 default:
354 Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
355 + valueName[0]);
356 break;
357 }
358 }
359 if (values != null) {
360 synchronized (mLock) {
361 mSettings.putAll(values);
362 // Invoke all the registered listeners.
363 invokeAllListeners();
364 }
365 }
366 }
367
368 @Override
369 public void resetData() {
370 synchronized (mLock) {
371 mSettings.clear();
372 }
373 }
374
375 @Override
376 public boolean hasNewDataToSerialize() {
377 return mHasNewDataToSerialize;
378 }
379
380 @Override
381 public String getName() {
382 return XML_TAG_SECTION_HEADER;
383 }
384
385 @Override
386 public @WifiConfigStore.StoreFileId int getStoreFileId() {
387 // Shared general store.
Roshan Pius0412d2d2020-02-10 13:26:42 -0800388 return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
Roshan Piusb790f672020-02-14 07:06:13 -0800389 }
390 }
391}