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