blob: 7a1931cf20364f59651a6e7b66159ab0f019bbd9 [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;
28import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
29import com.android.server.wifi.util.XmlUtil;
30
Roshan Piusb790f672020-02-14 07:06:13 -080031import org.xmlpull.v1.XmlPullParser;
32import org.xmlpull.v1.XmlPullParserException;
33import org.xmlpull.v1.XmlSerializer;
34
Roshan Piusda0d1992020-03-04 08:34:42 -080035import java.io.FileDescriptor;
Roshan Piusb790f672020-02-14 07:06:13 -080036import java.io.IOException;
Roshan Piusda0d1992020-03-04 08:34:42 -080037import java.io.PrintWriter;
Roshan Pius67ee4292020-02-25 09:51:02 -080038import java.util.ArrayList;
Roshan Piusb790f672020-02-14 07:06:13 -080039import java.util.HashMap;
40import java.util.Map;
41
42/**
43 * Store data for storing wifi settings. These are key (string) / value pairs that are stored in
44 * WifiConfigStore.xml file in a separate section.
45 */
46public class WifiSettingsConfigStore {
47 private static final String TAG = "WifiSettingsConfigStore";
48
Roshan Pius67ee4292020-02-25 09:51:02 -080049 // List of all allowed keys.
50 private static final ArrayList<Key> sKeys = new ArrayList<>();
51
Roshan Piusb790f672020-02-14 07:06:13 -080052 /******** Wifi shared pref keys ***************/
Roshan Pius0412d2d2020-02-10 13:26:42 -080053 /**
54 * Indicate whether factory reset request is pending.
55 */
Roshan Pius67ee4292020-02-25 09:51:02 -080056 public static final Key<Boolean> WIFI_P2P_PENDING_FACTORY_RESET =
57 new Key<>("wifi_p2p_pending_factory_reset", false);
Roshan Pius0412d2d2020-02-10 13:26:42 -080058
59 /**
60 * Allow scans to be enabled even wifi is turned off.
61 */
Roshan Pius67ee4292020-02-25 09:51:02 -080062 public static final Key<Boolean> WIFI_SCAN_ALWAYS_AVAILABLE =
63 new Key<>("wifi_scan_always_enabled", false);
Roshan Pius0412d2d2020-02-10 13:26:42 -080064
65 /**
66 * Whether wifi scan throttle is enabled or not.
67 */
Roshan Pius67ee4292020-02-25 09:51:02 -080068 public static final Key<Boolean> WIFI_SCAN_THROTTLE_ENABLED =
69 new Key<>("wifi_scan_throttle_enabled", true);
Roshan Pius0412d2d2020-02-10 13:26:42 -080070
71 /**
72 * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
73 * will enable it. In the future, additional values may be supported.
74 */
Roshan Pius67ee4292020-02-25 09:51:02 -080075 public static final Key<Boolean> WIFI_VERBOSE_LOGGING_ENABLED =
76 new Key<>("wifi_verbose_logging_enabled", false);
77
78 /**
79 * The Wi-Fi peer-to-peer device name
80 */
81 public static final Key<String> WIFI_P2P_DEVICE_NAME =
82 new Key<>("wifi_p2p_device_name", null);
83
Roshan Piusb790f672020-02-14 07:06:13 -080084 /******** Wifi shared pref keys ***************/
85
86 private final Context mContext;
87 private final Handler mHandler;
88 private final WifiConfigManager mWifiConfigManager;
89
90 private final Object mLock = new Object();
91 @GuardedBy("mLock")
92 private final Map<String, Object> mSettings = new HashMap<>();
93 @GuardedBy("mLock")
94 private final Map<String, Map<OnSettingsChangedListener, Handler>> mListeners =
95 new HashMap<>();
Roshan Piusda0d1992020-03-04 08:34:42 -080096 private WifiMigration.SettingsMigrationData mCachedMigrationData = null;
Roshan Piusb790f672020-02-14 07:06:13 -080097
98 private boolean mHasNewDataToSerialize = false;
99
100 /**
101 * Interface for a settings change listener.
Roshan Pius67ee4292020-02-25 09:51:02 -0800102 * @param <T> Type of the value.
Roshan Piusb790f672020-02-14 07:06:13 -0800103 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800104 public interface OnSettingsChangedListener<T> {
Roshan Piusb790f672020-02-14 07:06:13 -0800105 /**
106 * Invoked when a particular key settings changes.
107 *
108 * @param key Key that was changed.
109 * @param newValue New value that was assigned to the key.
110 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800111 void onSettingsChanged(@NonNull Key<T> key, @Nullable T newValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800112 }
113
114 public WifiSettingsConfigStore(@NonNull Context context, @NonNull Handler handler,
115 @NonNull WifiConfigManager wifiConfigManager,
116 @NonNull WifiConfigStore wifiConfigStore) {
117 mContext = context;
118 mHandler = handler;
119 mWifiConfigManager = wifiConfigManager;
120
121 // Register our data store.
122 wifiConfigStore.registerStoreData(new StoreData());
123 }
124
125 private void invokeAllListeners() {
126 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800127 for (Key key : sKeys) {
Roshan Piusb790f672020-02-14 07:06:13 -0800128 invokeListeners(key);
129 }
130 }
131 }
132
Roshan Pius67ee4292020-02-25 09:51:02 -0800133 private <T> void invokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800134 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800135 if (!mSettings.containsKey(key.key)) return;
136 Object newValue = mSettings.get(key.key);
137 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800138 if (listeners == null || listeners.isEmpty()) return;
139 for (Map.Entry<OnSettingsChangedListener, Handler> listener
140 : listeners.entrySet()) {
141 // Trigger the callback in the appropriate handler.
Roshan Pius67ee4292020-02-25 09:51:02 -0800142 listener.getValue().post(() ->
143 listener.getKey().onSettingsChanged(key, newValue));
Roshan Piusb790f672020-02-14 07:06:13 -0800144 }
145 }
146 }
147
148 /**
149 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
150 */
Roshan Pius0412d2d2020-02-10 13:26:42 -0800151 private void triggerSaveToStoreAndInvokeAllListeners() {
152 mHandler.post(() -> {
153 mHasNewDataToSerialize = true;
154 mWifiConfigManager.saveToStore(true);
155
156 invokeAllListeners();
157 });
158 }
159
160 /**
161 * Trigger config store writes and invoke listeners in the main wifi service looper's handler.
162 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800163 private <T> void triggerSaveToStoreAndInvokeListeners(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800164 mHandler.post(() -> {
165 mHasNewDataToSerialize = true;
166 mWifiConfigManager.saveToStore(true);
167
168 invokeListeners(key);
169 });
170 }
171
172 /**
Roshan Pius0412d2d2020-02-10 13:26:42 -0800173 * Performs a one time migration from Settings.Global values to settings store. Only
174 * performed one time if the settings store is empty.
175 */
176 private void migrateFromSettingsIfNeeded() {
177 if (!mSettings.isEmpty()) return; // already migrated.
178
Roshan Piusda0d1992020-03-04 08:34:42 -0800179 mCachedMigrationData = WifiMigration.loadFromSettings(mContext);
180 if (mCachedMigrationData == null) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800181 Log.e(TAG, "No settings data to migrate");
Roshan Pius0412d2d2020-02-10 13:26:42 -0800182 return;
183 }
184 Log.i(TAG, "Migrating data out of settings to shared preferences");
185
Roshan Pius67ee4292020-02-25 09:51:02 -0800186 mSettings.put(WIFI_P2P_DEVICE_NAME.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800187 mCachedMigrationData.getP2pDeviceName());
Roshan Pius67ee4292020-02-25 09:51:02 -0800188 mSettings.put(WIFI_P2P_PENDING_FACTORY_RESET.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800189 mCachedMigrationData.isP2pFactoryResetPending());
Roshan Pius67ee4292020-02-25 09:51:02 -0800190 mSettings.put(WIFI_SCAN_ALWAYS_AVAILABLE.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800191 mCachedMigrationData.isScanAlwaysAvailable());
Roshan Pius67ee4292020-02-25 09:51:02 -0800192 mSettings.put(WIFI_SCAN_THROTTLE_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800193 mCachedMigrationData.isScanThrottleEnabled());
Roshan Pius67ee4292020-02-25 09:51:02 -0800194 mSettings.put(WIFI_VERBOSE_LOGGING_ENABLED.key,
Roshan Piusda0d1992020-03-04 08:34:42 -0800195 mCachedMigrationData.isVerboseLoggingEnabled());
Roshan Pius0412d2d2020-02-10 13:26:42 -0800196 triggerSaveToStoreAndInvokeAllListeners();
197 }
198
199 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800200 * Store a value to the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800201 *
202 * @param key One of the settings keys.
203 * @param value Value to be stored.
204 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800205 public <T> void put(@NonNull Key<T> key, @Nullable T value) {
Roshan Piusb790f672020-02-14 07:06:13 -0800206 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800207 mSettings.put(key.key, value);
Roshan Piusb790f672020-02-14 07:06:13 -0800208 }
209 triggerSaveToStoreAndInvokeListeners(key);
210 }
211
212 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800213 * Retrieve a value from the stored settings.
Roshan Piusb790f672020-02-14 07:06:13 -0800214 *
215 * @param key One of the settings keys.
Roshan Piusb790f672020-02-14 07:06:13 -0800216 * @return value stored in settings, defValue if the key does not exist.
217 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800218 public @Nullable <T> T get(@NonNull Key<T> key) {
Roshan Piusb790f672020-02-14 07:06:13 -0800219 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800220 return (T) mSettings.getOrDefault(key.key, key.defaultValue);
Roshan Piusb790f672020-02-14 07:06:13 -0800221 }
222 }
223
224 /**
225 * Register for settings change listener.
226 *
227 * @param key One of the settings keys.
228 * @param listener Listener to be registered.
229 * @param handler Handler to post the listener
230 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800231 public <T> void registerChangeListener(@NonNull Key<T> key,
232 @NonNull OnSettingsChangedListener<T> listener, @NonNull Handler handler) {
Roshan Piusb790f672020-02-14 07:06:13 -0800233 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800234 mListeners.computeIfAbsent(
235 key.key, ignore -> new HashMap<>()).put(listener, handler);
Roshan Piusb790f672020-02-14 07:06:13 -0800236 }
237 }
238
239 /**
240 * Unregister for settings change listener.
241 *
242 * @param key One of the settings keys.
243 * @param listener Listener to be unregistered.
244 */
Roshan Pius67ee4292020-02-25 09:51:02 -0800245 public <T> void unregisterChangeListener(@NonNull Key<T> key,
246 @NonNull OnSettingsChangedListener<T> listener) {
Roshan Piusb790f672020-02-14 07:06:13 -0800247 synchronized (mLock) {
Roshan Pius67ee4292020-02-25 09:51:02 -0800248 Map<OnSettingsChangedListener, Handler> listeners = mListeners.get(key.key);
Roshan Piusb790f672020-02-14 07:06:13 -0800249 if (listeners == null || listeners.isEmpty()) {
250 Log.e(TAG, "No listeners for " + key);
251 return;
252 }
253 if (listeners.remove(listener) == null) {
254 Log.e(TAG, "Unknown listener for " + key);
255 }
256 }
257 }
258
259 /**
Roshan Piusda0d1992020-03-04 08:34:42 -0800260 * Dump output for debugging.
261 */
262 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
263 pw.println();
264 pw.println("Dump of " + TAG);
265 pw.println("Settings:");
266 for (Map.Entry<String, Object> entry : mSettings.entrySet()) {
267 pw.print(entry.getKey());
268 pw.print("=");
269 pw.println(entry.getValue());
270 }
271 if (mCachedMigrationData == null) return;
272 pw.println("Migration data:");
273 pw.print(WIFI_P2P_DEVICE_NAME.key);
274 pw.print("=");
275 pw.println(mCachedMigrationData.getP2pDeviceName());
276 pw.print(WIFI_P2P_PENDING_FACTORY_RESET.key);
277 pw.print("=");
278 pw.println(mCachedMigrationData.isP2pFactoryResetPending());
279 pw.print(WIFI_SCAN_ALWAYS_AVAILABLE.key);
280 pw.print("=");
281 pw.println(mCachedMigrationData.isScanAlwaysAvailable());
282 pw.print(WIFI_SCAN_THROTTLE_ENABLED.key);
283 pw.print("=");
284 pw.println(mCachedMigrationData.isScanThrottleEnabled());
285 pw.print(WIFI_VERBOSE_LOGGING_ENABLED.key);
286 pw.print("=");
287 pw.println(mCachedMigrationData.isVerboseLoggingEnabled());
288 pw.println();
289 }
290
291 /**
Roshan Pius67ee4292020-02-25 09:51:02 -0800292 * Base class to store string key and its default value.
293 * @param <T> Type of the value.
294 */
295 public static class Key<T> {
296 public final String key;
297 public final T defaultValue;
298
299 private Key(@NonNull String key, T defaultValue) {
300 this.key = key;
301 this.defaultValue = defaultValue;
302 sKeys.add(this);
303 }
304
305 @Override
306 public String toString() {
307 return "[Key " + key + ", DefaultValue: " + defaultValue + "]";
308 }
309 }
310
311 /**
Roshan Piusb790f672020-02-14 07:06:13 -0800312 * Store data for persisting the settings data to config store.
313 */
314 private class StoreData implements WifiConfigStore.StoreData {
315 private static final String XML_TAG_SECTION_HEADER = "Settings";
316 private static final String XML_TAG_VALUES = "Values";
317
318 @Override
319 public void serializeData(XmlSerializer out,
320 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
321 throws XmlPullParserException, IOException {
322 synchronized (mLock) {
323 XmlUtil.writeNextValue(out, XML_TAG_VALUES, mSettings);
324 }
325 }
326
327 @Override
328 public void deserializeData(XmlPullParser in, int outerTagDepth,
329 @WifiConfigStore.Version int version,
Roshan Pius5ed19b92020-02-24 13:15:00 -0800330 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil,
331 @NonNull WifiConfigStoreMigrationDataHolder storeMigrationDataHolder)
Roshan Piusb790f672020-02-14 07:06:13 -0800332 throws XmlPullParserException, IOException {
Roshan Piusb790f672020-02-14 07:06:13 -0800333 if (in == null) {
Roshan Pius0412d2d2020-02-10 13:26:42 -0800334 // Empty read triggers the migration since it indicates that there is no settings
335 // data stored in the settings store.
336 migrateFromSettingsIfNeeded();
Roshan Piusb790f672020-02-14 07:06:13 -0800337 return;
338 }
339 Map<String, Object> values = null;
340 while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
341 String[] valueName = new String[1];
342 Object value = XmlUtil.readCurrentValue(in, valueName);
343 if (TextUtils.isEmpty(valueName[0])) {
344 throw new XmlPullParserException("Missing value name");
345 }
346 switch (valueName[0]) {
347 case XML_TAG_VALUES:
348 values = (Map) value;
349 break;
350 default:
351 Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER + ": "
352 + valueName[0]);
353 break;
354 }
355 }
356 if (values != null) {
357 synchronized (mLock) {
358 mSettings.putAll(values);
359 // Invoke all the registered listeners.
360 invokeAllListeners();
361 }
362 }
363 }
364
365 @Override
366 public void resetData() {
367 synchronized (mLock) {
368 mSettings.clear();
369 }
370 }
371
372 @Override
373 public boolean hasNewDataToSerialize() {
374 return mHasNewDataToSerialize;
375 }
376
377 @Override
378 public String getName() {
379 return XML_TAG_SECTION_HEADER;
380 }
381
382 @Override
383 public @WifiConfigStore.StoreFileId int getStoreFileId() {
384 // Shared general store.
Roshan Pius0412d2d2020-02-10 13:26:42 -0800385 return WifiConfigStore.STORE_FILE_SHARED_GENERAL;
Roshan Piusb790f672020-02-14 07:06:13 -0800386 }
387 }
388}