diff options
11 files changed, 665 insertions, 188 deletions
diff --git a/service/java/com/android/server/wifi/SoftApStoreData.java b/service/java/com/android/server/wifi/SoftApStoreData.java new file mode 100644 index 000000000..2879c2aad --- /dev/null +++ b/service/java/com/android/server/wifi/SoftApStoreData.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.annotation.Nullable; +import android.net.wifi.WifiConfiguration; +import android.text.TextUtils; +import android.util.Log; + +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; +import com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.BitSet; + +/** + * Store data for SoftAp + */ +public class SoftApStoreData implements WifiConfigStore.StoreData { + private static final String TAG = "SoftApStoreData"; + private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp"; + private static final String XML_TAG_SSID = "SSID"; + private static final String XML_TAG_BAND = "Band"; + private static final String XML_TAG_CHANNEL = "Channel"; + private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID"; + private static final String XML_TAG_ALLOWED_KEY_MGMT = "AllowedKeyMgmt"; + private static final String XML_TAG_PRE_SHARED_KEY = "PreSharedKey"; + + private final DataSource mDataSource; + + /** + * Interface define the data source for the notifier store data. + */ + public interface DataSource { + /** + * Retrieve the SoftAp configuration from the data source to serialize them to disk. + * + * @return {@link WifiConfiguration} Instance of WifiConfiguration. + */ + WifiConfiguration toSerialize(); + + /** + * Set the SoftAp configuration in the data source after serializing them from disk. + * + * @param config {@link WifiConfiguration} Instance of WifiConfiguration. + */ + void fromDeserialized(WifiConfiguration config); + + /** + * Clear internal data structure in preparation for user switch or initial store read. + */ + void reset(); + + /** + * Indicates whether there is new data to serialize. + */ + boolean hasNewDataToSerialize(); + } + + /** + * Creates the SSID Set store data. + * + * @param dataSource The DataSource that implements the update and retrieval of the SSID set. + */ + SoftApStoreData(DataSource dataSource) { + mDataSource = dataSource; + } + + @Override + public void serializeData(XmlSerializer out, + @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) + throws XmlPullParserException, IOException { + WifiConfiguration softApConfig = mDataSource.toSerialize(); + if (softApConfig != null) { + XmlUtil.writeNextValue(out, XML_TAG_SSID, softApConfig.SSID); + XmlUtil.writeNextValue(out, XML_TAG_BAND, softApConfig.apBand); + XmlUtil.writeNextValue(out, XML_TAG_CHANNEL, softApConfig.apChannel); + XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, softApConfig.hiddenSSID); + XmlUtil.writeNextValue( + out, XML_TAG_ALLOWED_KEY_MGMT, + softApConfig.allowedKeyManagement.toByteArray()); + XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, softApConfig.preSharedKey); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) + throws XmlPullParserException, IOException { + // Ignore empty reads. + if (in == null) { + return; + } + WifiConfiguration softApConfig = new WifiConfiguration(); + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (TextUtils.isEmpty(valueName[0])) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_SSID: + softApConfig.SSID = (String) value; + break; + case XML_TAG_BAND: + softApConfig.apBand = (int) value; + break; + case XML_TAG_CHANNEL: + softApConfig.apChannel = (int) value; + break; + case XML_TAG_HIDDEN_SSID: + softApConfig.hiddenSSID = (boolean) value; + break; + case XML_TAG_ALLOWED_KEY_MGMT: + byte[] allowedKeyMgmt = (byte[]) value; + softApConfig.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt); + break; + case XML_TAG_PRE_SHARED_KEY: + softApConfig.preSharedKey = (String) value; + break; + default: + Log.w(TAG, "Ignoring unknown value name " + valueName[0]); + break; + } + } + // We should at-least have SSID restored from store. + if (softApConfig.SSID == null) { + Log.e(TAG, "Failed to parse SSID"); + return; + } + mDataSource.fromDeserialized(softApConfig); + } + + @Override + public void resetData() { + mDataSource.reset(); + } + + @Override + public boolean hasNewDataToSerialize() { + return mDataSource.hasNewDataToSerialize(); + } + + @Override + public String getName() { + return XML_TAG_SECTION_HEADER_SOFTAP; + } + + @Override + public @WifiConfigStore.StoreFileId int getStoreFileId() { + return WifiConfigStore.STORE_FILE_SHARED_SOFTAP; // Shared softap store. + } +} diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index 7cb7a4cef..48ad9b1c0 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -39,11 +39,10 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.wifi.R; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.DataInputStream; -import java.io.DataOutputStream; +import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; @@ -64,10 +63,13 @@ public class WifiApConfigStore { private static final String TAG = "WifiApConfigStore"; - private static final String DEFAULT_AP_CONFIG_FILE = + // Note: This is the legacy Softap config file. This is only used for migrating data out + // of this file on first reboot. + private static final String LEGACY_AP_CONFIG_FILE = Environment.getDataDirectory() + "/misc/wifi/softap.conf"; - private static final int AP_CONFIG_FILE_VERSION = 3; + @VisibleForTesting + public static final int AP_CONFIG_FILE_VERSION = 3; private static final int RAND_SSID_INT_MIN = 1000; private static final int RAND_SSID_INT_MAX = 9999; @@ -84,24 +86,57 @@ public class WifiApConfigStore { @VisibleForTesting static final int AP_CHANNEL_DEFAULT = 0; - private WifiConfiguration mWifiApConfig = null; + private WifiConfiguration mPersistentWifiApConfig = null; private ArrayList<Integer> mAllowed2GChannel = null; private final Context mContext; private final WifiInjector mWifiInjector; private final Handler mHandler; - private final String mApConfigFile; private final BackupManagerProxy mBackupManagerProxy; private final FrameworkFacade mFrameworkFacade; private final MacAddressUtil mMacAddressUtil; private final Mac mMac; + private final WifiConfigStore mWifiConfigStore; + private final WifiConfigManager mWifiConfigManager; private boolean mRequiresApBandConversion = false; + private boolean mHasNewDataToSerialize = false; + + /** + * Module to interact with the wifi config store. + */ + private class SoftApStoreDataSource implements SoftApStoreData.DataSource { + + public WifiConfiguration toSerialize() { + mHasNewDataToSerialize = false; + return mPersistentWifiApConfig; + } + + public void fromDeserialized(WifiConfiguration config) { + mPersistentWifiApConfig = new WifiConfiguration(config); + } + + public void reset() { + if (mPersistentWifiApConfig != null) { + // Note: Reset is invoked when WifiConfigStore.read() is invoked on boot completed. + // If we had migrated data from the legacy store before that (which is most likely + // true because we read the legacy file in the constructor here, whereas + // WifiConfigStore.read() is only triggered on boot completed), trigger a write to + // persist the migrated data. + mHandler.post(() -> mWifiConfigManager.saveToStore(true)); + } + } + + public boolean hasNewDataToSerialize() { + return mHasNewDataToSerialize; + } + } WifiApConfigStore(Context context, WifiInjector wifiInjector, Handler handler, - BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade) { - this(context, wifiInjector, handler, backupManagerProxy, frameworkFacade, - DEFAULT_AP_CONFIG_FILE); + BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade, + WifiConfigStore wifiConfigStore, WifiConfigManager wifiConfigManager) { + this(context, wifiInjector, handler, backupManagerProxy, frameworkFacade, wifiConfigStore, + wifiConfigManager, LEGACY_AP_CONFIG_FILE); } WifiApConfigStore(Context context, @@ -109,13 +144,16 @@ public class WifiApConfigStore { Handler handler, BackupManagerProxy backupManagerProxy, FrameworkFacade frameworkFacade, + WifiConfigStore wifiConfigStore, + WifiConfigManager wifiConfigManager, String apConfigFile) { mContext = context; mWifiInjector = wifiInjector; mHandler = handler; mBackupManagerProxy = backupManagerProxy; mFrameworkFacade = frameworkFacade; - mApConfigFile = apConfigFile; + mWifiConfigStore = wifiConfigStore; + mWifiConfigManager = wifiConfigManager; String ap2GChannelListStr = mContext.getResources().getString( R.string.config_wifi_framework_sap_2G_channel_list); @@ -132,17 +170,27 @@ public class WifiApConfigStore { mRequiresApBandConversion = mContext.getResources().getBoolean( R.bool.config_wifi_convert_apband_5ghz_to_any); - /* Load AP configuration from persistent storage. */ - mWifiApConfig = loadApConfiguration(mApConfigFile); - if (mWifiApConfig == null) { - /* Use default configuration. */ - Log.d(TAG, "Fallback to use default AP configuration"); - mWifiApConfig = getDefaultApConfiguration(); - - /* Save the default configuration to persistent storage. */ - writeApConfiguration(mApConfigFile, mWifiApConfig); + // One time migration from legacy config store. + try { + File file = new File(apConfigFile); + FileInputStream fis = new FileInputStream(apConfigFile); + /* Load AP configuration from persistent storage. */ + WifiConfiguration config = loadApConfigurationFromLegacyFile(fis); + if (config != null) { + // Persist in the new store. + persistConfigAndTriggerBackupManagerProxy(config); + Log.i(TAG, "Migrated data out of legacy store file " + apConfigFile); + // delete the legacy file. + file.delete(); + } + } catch (FileNotFoundException e) { + // Expected on further reboots after the first reboot. } + // Register store data listener + mWifiConfigStore.registerStoreData( + mWifiInjector.makeSoftApStoreData(new SoftApStoreDataSource())); + IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT); mContext.registerReceiver( @@ -176,13 +224,18 @@ public class WifiApConfigStore { * Return the current soft access point configuration. */ public synchronized WifiConfiguration getApConfiguration() { - WifiConfiguration config = sanitizePersistentApConfig(mWifiApConfig); - if (mWifiApConfig != config) { + if (mPersistentWifiApConfig == null) { + /* Use default configuration. */ + Log.d(TAG, "Fallback to use default AP configuration"); + persistConfigAndTriggerBackupManagerProxy(getDefaultApConfiguration()); + } + WifiConfiguration sanitizedPersistentconfig = + sanitizePersistentApConfig(mPersistentWifiApConfig); + if (mPersistentWifiApConfig != sanitizedPersistentconfig) { Log.d(TAG, "persisted config was converted, need to resave it"); - mWifiApConfig = config; - persistConfigAndTriggerBackupManagerProxy(mWifiApConfig); + persistConfigAndTriggerBackupManagerProxy(sanitizedPersistentconfig); } - return mWifiApConfig; + return mPersistentWifiApConfig; } /** @@ -193,11 +246,11 @@ public class WifiApConfigStore { */ public synchronized void setApConfiguration(WifiConfiguration config) { if (config == null) { - mWifiApConfig = getDefaultApConfiguration(); + config = getDefaultApConfiguration(); } else { - mWifiApConfig = sanitizePersistentApConfig(config); + config = sanitizePersistentApConfig(config); } - persistConfigAndTriggerBackupManagerProxy(mWifiApConfig); + persistConfigAndTriggerBackupManagerProxy(config); } public ArrayList<Integer> getAllowed2GChannel() { @@ -280,21 +333,22 @@ public class WifiApConfigStore { } private void persistConfigAndTriggerBackupManagerProxy(WifiConfiguration config) { - writeApConfiguration(mApConfigFile, mWifiApConfig); - // Stage the backup of the SettingsProvider package which backs this up + mPersistentWifiApConfig = config; + mHasNewDataToSerialize = true; + mWifiConfigManager.saveToStore(true); mBackupManagerProxy.notifyDataChanged(); } /** - * Load AP configuration from persistent storage. + * Load AP configuration from legacy persistent storage. + * Note: This is deprecated and only used for migrating data once on reboot. */ - private static WifiConfiguration loadApConfiguration(final String filename) { + private static WifiConfiguration loadApConfigurationFromLegacyFile(FileInputStream fis) { WifiConfiguration config = null; DataInputStream in = null; try { config = new WifiConfiguration(); - in = new DataInputStream( - new BufferedInputStream(new FileInputStream(filename))); + in = new DataInputStream(new BufferedInputStream(fis)); int version = in.readInt(); if (version < 1 || version > AP_CONFIG_FILE_VERSION) { @@ -333,28 +387,6 @@ public class WifiApConfigStore { } /** - * Write AP configuration to persistent storage. - */ - private static void writeApConfiguration(final String filename, - final WifiConfiguration config) { - try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream( - new FileOutputStream(filename)))) { - out.writeInt(AP_CONFIG_FILE_VERSION); - out.writeUTF(config.SSID); - out.writeInt(config.apBand); - out.writeInt(config.apChannel); - out.writeBoolean(config.hiddenSSID); - int authType = config.getAuthType(); - out.writeInt(authType); - if (authType != KeyMgmt.NONE) { - out.writeUTF(config.preSharedKey); - } - } catch (IOException e) { - Log.e(TAG, "Error writing hotspot configuration" + e); - } - } - - /** * Generate a default WPA2 based configuration with a random password. * We are changing the Wifi Ap configuration storage from secure settings to a * flat file accessible only by the system. A WPA2 based default configuration diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index 7696d73b4..cff7b7fe6 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -81,16 +81,21 @@ public class WifiConfigStore { */ public static final int STORE_FILE_SHARED_GENERAL = 0; /** + * Config store file for softap shared store file. + */ + public static final int STORE_FILE_SHARED_SOFTAP = 1; + /** * Config store file for general user store file. */ - public static final int STORE_FILE_USER_GENERAL = 1; + public static final int STORE_FILE_USER_GENERAL = 2; /** * Config store file for network suggestions user store file. */ - public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 2; + public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 3; @IntDef(prefix = { "STORE_FILE_" }, value = { STORE_FILE_SHARED_GENERAL, + STORE_FILE_SHARED_SOFTAP, STORE_FILE_USER_GENERAL, STORE_FILE_USER_NETWORK_SUGGESTIONS }) @@ -153,6 +158,10 @@ public class WifiConfigStore { */ private static final String STORE_FILE_NAME_SHARED_GENERAL = "WifiConfigStore.xml"; /** + * Config store file name for SoftAp shared store file. + */ + private static final String STORE_FILE_NAME_SHARED_SOFTAP = "WifiConfigStoreSoftAp.xml"; + /** * Config store file name for general user store file. */ private static final String STORE_FILE_NAME_USER_GENERAL = "WifiConfigStore.xml"; @@ -167,6 +176,7 @@ public class WifiConfigStore { private static final SparseArray<String> STORE_ID_TO_FILE_NAME = new SparseArray<String>() {{ put(STORE_FILE_SHARED_GENERAL, STORE_FILE_NAME_SHARED_GENERAL); + put(STORE_FILE_SHARED_SOFTAP, STORE_FILE_NAME_SHARED_SOFTAP); put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL); put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS); }}; @@ -184,10 +194,10 @@ public class WifiConfigStore { private final Clock mClock; private final WifiMetrics mWifiMetrics; /** - * Shared config store file instance. There is 1 shared store file: - * {@link #STORE_FILE_NAME_SHARED_GENERAL}. + * Shared config store file instance. There are 2 shared store files: + * {@link #STORE_FILE_NAME_SHARED_GENERAL} & {@link #STORE_FILE_NAME_SHARED_SOFTAP}. */ - private StoreFile mSharedStore; + private final List<StoreFile> mSharedStores; /** * User specific store file instances. There are 2 user store files: * {@link #STORE_FILE_NAME_USER_GENERAL} & {@link #STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS}. @@ -228,11 +238,12 @@ public class WifiConfigStore { * @param handler handler instance to post alarm timeouts to. * @param clock clock instance to retrieve timestamps for alarms. * @param wifiMetrics Metrics instance. - * @param sharedStore StoreFile instance pointing to the shared store file. This should - * be retrieved using {@link #createSharedFile()} method. + * @param sharedStores List of {@link StoreFile} instances pointing to the shared store files. + * This should be retrieved using {@link #createSharedFiles(boolean)} + * method. */ public WifiConfigStore(Context context, Handler handler, Clock clock, WifiMetrics wifiMetrics, - StoreFile sharedStore) { + List<StoreFile> sharedStores) { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mEventHandler = handler; @@ -241,7 +252,7 @@ public class WifiConfigStore { mStoreDataList = new ArrayList<>(); // Initialize the store files. - mSharedStore = sharedStore; + mSharedStores = sharedStores; // The user store is initialized to null, this will be set when the user unlocks and // CE storage is accessible via |switchUserStoresAndRead|. mUserStores = null; @@ -250,7 +261,8 @@ public class WifiConfigStore { /** * Set the user store files. * (Useful for mocking in unit tests). - * @param userStores List of {@link StoreFile} created using {@link #createUserFiles(int)}. + * @param userStores List of {@link StoreFile} created using + * {@link #createUserFiles(int, boolean)}. */ public void setUserStores(@NonNull List<StoreFile> userStores) { Preconditions.checkNotNull(userStores); @@ -307,14 +319,29 @@ public class WifiConfigStore { return new StoreFile(file, fileId, encryptionUtil); } + private static @Nullable List<StoreFile> createFiles(File storeBaseDir, + List<Integer> storeFileIds, boolean shouldEncryptCredentials) { + List<StoreFile> storeFiles = new ArrayList<>(); + for (int fileId : storeFileIds) { + StoreFile storeFile = createFile(storeBaseDir, fileId, shouldEncryptCredentials); + if (storeFile == null) { + return null; + } + storeFiles.add(storeFile); + } + return storeFiles; + } + /** * Create a new instance of the shared store file. * * @param shouldEncryptCredentials Whether to encrypt credentials or not. * @return new instance of the store file or null if the directory cannot be created. */ - public static @Nullable StoreFile createSharedFile(boolean shouldEncryptCredentials) { - return createFile(Environment.getDataMiscDirectory(), STORE_FILE_SHARED_GENERAL, + public static @NonNull List<StoreFile> createSharedFiles(boolean shouldEncryptCredentials) { + return createFiles( + Environment.getDataMiscDirectory(), + Arrays.asList(STORE_FILE_SHARED_GENERAL, STORE_FILE_SHARED_SOFTAP), shouldEncryptCredentials); } @@ -329,18 +356,10 @@ public class WifiConfigStore { */ public static @Nullable List<StoreFile> createUserFiles(int userId, boolean shouldEncryptCredentials) { - List<StoreFile> storeFiles = new ArrayList<>(); - for (int fileId : Arrays.asList( - STORE_FILE_USER_GENERAL, STORE_FILE_USER_NETWORK_SUGGESTIONS)) { - StoreFile storeFile = - createFile(Environment.getDataMiscCeDirectory(userId), fileId, - shouldEncryptCredentials); - if (storeFile == null) { - return null; - } - storeFiles.add(storeFile); - } - return storeFiles; + return createFiles( + Environment.getDataMiscCeDirectory(userId), + Arrays.asList(STORE_FILE_USER_GENERAL, STORE_FILE_USER_NETWORK_SUGGESTIONS), + shouldEncryptCredentials); } /** @@ -351,18 +370,6 @@ public class WifiConfigStore { } /** - * API to check if any of the store files are present on the device. This can be used - * to detect if the device needs to perform data migration from legacy stores. - * - * @return true if any of the store file is present, false otherwise. - */ - public boolean areStoresPresent() { - // Checking for the shared store file existence is sufficient since this is guaranteed - // to be present on migrated devices. - return mSharedStore.exists(); - } - - /** * Retrieve the list of {@link StoreData} instances registered for the provided * {@link StoreFile}. */ @@ -395,10 +402,12 @@ public class WifiConfigStore { boolean hasAnyNewData = false; // Serialize the provided data and send it to the respective stores. The actual write will // be performed later depending on the |forceSync| flag . - if (hasNewDataToSerialize(mSharedStore)) { - byte[] sharedDataBytes = serializeData(mSharedStore); - mSharedStore.storeRawDataToWrite(sharedDataBytes); - hasAnyNewData = true; + for (StoreFile sharedStoreFile : mSharedStores) { + if (hasNewDataToSerialize(sharedStoreFile)) { + byte[] sharedDataBytes = serializeData(sharedStoreFile); + sharedStoreFile.storeRawDataToWrite(sharedDataBytes); + hasAnyNewData = true; + } } if (mUserStores != null) { for (StoreFile userStoreFile : mUserStores) { @@ -489,7 +498,9 @@ public class WifiConfigStore { stopBufferedWriteAlarm(); long writeStartTime = mClock.getElapsedSinceBootMillis(); - mSharedStore.writeBufferedRawData(); + for (StoreFile sharedStoreFile : mSharedStores) { + sharedStoreFile.writeBufferedRawData(); + } if (mUserStores != null) { for (StoreFile userStoreFile : mUserStores) { userStoreFile.writeBufferedRawData(); @@ -511,7 +522,9 @@ public class WifiConfigStore { */ public void read() throws XmlPullParserException, IOException { // Reset both share and user store data. - resetStoreData(mSharedStore); + for (StoreFile sharedStoreFile : mSharedStores) { + resetStoreData(sharedStoreFile); + } if (mUserStores != null) { for (StoreFile userStoreFile : mUserStores) { resetStoreData(userStoreFile); @@ -519,8 +532,10 @@ public class WifiConfigStore { } long readStartTime = mClock.getElapsedSinceBootMillis(); - byte[] sharedDataBytes = mSharedStore.readRawData(); - deserializeData(sharedDataBytes, mSharedStore); + for (StoreFile sharedStoreFile : mSharedStores) { + byte[] sharedDataBytes = sharedStoreFile.readRawData(); + deserializeData(sharedDataBytes, sharedStoreFile); + } if (mUserStores != null) { for (StoreFile userStoreFile : mUserStores) { byte[] userDataBytes = userStoreFile.readRawData(); @@ -681,11 +696,13 @@ public class WifiConfigStore { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Dump of WifiConfigStore"); pw.println("WifiConfigStore - Store File Begin ----"); - Stream.of(Arrays.asList(mSharedStore), mUserStores) + Stream.of(mSharedStores, mUserStores) .flatMap(List::stream) .forEach((storeFile) -> { pw.print("Name: " + storeFile.mFileName); - pw.println(", Credentials encrypted: " + storeFile.getEncryptionUtil() != null); + pw.print(", File Id: " + storeFile.mFileId); + pw.println(", Credentials encrypted: " + + (storeFile.getEncryptionUtil() != null)); }); pw.println("WifiConfigStore - Store Data Begin ----"); for (StoreData storeData : mStoreDataList) { diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index a53c4ec32..683db73f6 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -241,9 +241,6 @@ public class WifiInjector { SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE), mContext.getResources() .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss)); - mWifiApConfigStore = new WifiApConfigStore( - mContext,this, wifiHandler, mBackupManagerProxy, mFrameworkFacade); - // WifiConfigManager/Store objects and their dependencies. KeyStore keyStore = null; try { @@ -254,7 +251,7 @@ public class WifiInjector { mWifiKeyStore = new WifiKeyStore(mKeyStore); // New config store mWifiConfigStore = new WifiConfigStore(mContext, wifiHandler, mClock, mWifiMetrics, - WifiConfigStore.createSharedFile(mFrameworkFacade.isNiapModeOn(mContext))); + WifiConfigStore.createSharedFiles(mFrameworkFacade.isNiapModeOn(mContext))); SubscriptionManager subscriptionManager = mContext.getSystemService(SubscriptionManager.class); mTelephonyUtil = new TelephonyUtil(makeTelephonyManager(), subscriptionManager); @@ -267,6 +264,11 @@ public class WifiInjector { new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(), mFrameworkFacade, wifiHandler, mDeviceConfigFacade); mWifiMetrics.setWifiConfigManager(mWifiConfigManager); + + mWifiApConfigStore = new WifiApConfigStore( + mContext, this, wifiHandler, mBackupManagerProxy, mFrameworkFacade, + mWifiConfigStore, mWifiConfigManager); + mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative); mConnectivityLocalLog = new LocalLog( mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? 256 : 512); @@ -654,6 +656,14 @@ public class WifiInjector { return new NetworkSuggestionStoreData(dataSource); } + /** + * Construct an instance of {@link SoftApStoreData}. + */ + public SoftApStoreData makeSoftApStoreData( + SoftApStoreData.DataSource dataSource) { + return new SoftApStoreData(dataSource); + } + public WifiPermissionsUtil getWifiPermissionsUtil() { return mWifiPermissionsUtil; } diff --git a/service/wifi.rc b/service/wifi.rc index 9a04252dc..5362ee69f 100644 --- a/service/wifi.rc +++ b/service/wifi.rc @@ -19,6 +19,7 @@ on post-fs-data chown network_stack network_stack /data/misc/wifi chown network_stack network_stack /data/misc/wifi/WifiConfigStore.xml + chown network_stack network_stack /data/misc/wifi/WifiConfigStoreSoftAp.xml chown network_stack network_stack /data/misc/wifi/softap.conf on property:sys.user.0.ce_available=true diff --git a/service/wifi_inprocess.rc b/service/wifi_inprocess.rc index 286d18063..43e4866b5 100644 --- a/service/wifi_inprocess.rc +++ b/service/wifi_inprocess.rc @@ -19,6 +19,7 @@ on post-fs-data chown system system /data/misc/wifi chown system system /data/misc/wifi/WifiConfigStore.xml + chown system system /data/misc/wifi/WifiConfigStoreSoftAp.xml chown system system /data/misc/wifi/softap.conf on property:sys.user.0.ce_available=true diff --git a/tests/wifitests/Android.bp b/tests/wifitests/Android.bp index 3bbbc46c2..341ab0982 100644 --- a/tests/wifitests/Android.bp +++ b/tests/wifitests/Android.bp @@ -250,6 +250,7 @@ android_test { "com.android.server.wifi.SoftApManager", "com.android.server.wifi.SoftApManager.*", "com.android.server.wifi.SoftApModeConfiguration", + "com.android.server.wifi.SoftApStoreData", "com.android.server.wifi.SsidSetStoreData", "com.android.server.wifi.SsidSetStoreData.*", "com.android.server.wifi.StateChangeResult", diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java new file mode 100644 index 000000000..577767c16 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.wifi.WifiConfiguration; +import android.util.Xml; + +import androidx.test.filters.SmallTest; + +import com.android.internal.util.FastXmlSerializer; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +/** + * Unit tests for {@link com.android.server.wifi.SoftApStoreData}. + */ +@SmallTest +public class SoftApStoreDataTest extends WifiBaseTest { + private static final String TEST_SSID = "SSID"; + private static final String TEST_PRESHARED_KEY = "Test"; + private static final boolean TEST_HIDDEN = false; + private static final int TEST_BAND = WifiConfiguration.AP_BAND_ANY; + private static final int TEST_CHANNEL = 0; + + private static final String TEST_SOFTAP_CONFIG_XML_STRING = + "<string name=\"SSID\">" + TEST_SSID + "</string>\n" + + "<int name=\"Band\" value=\"" + TEST_BAND + "\" />\n" + + "<int name=\"Channel\" value=\"" + TEST_CHANNEL + "\" />\n" + + "<boolean name=\"HiddenSSID\" value=\"" + TEST_HIDDEN + "\" />\n" + + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">02</byte-array>\n" + + "<string name=\"PreSharedKey\">" + TEST_PRESHARED_KEY + "</string>\n"; + + @Mock SoftApStoreData.DataSource mDataSource; + SoftApStoreData mSoftApStoreData; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mSoftApStoreData = new SoftApStoreData(mDataSource); + } + + /** + * Helper function for serializing configuration data to a XML block. + * + * @return byte[] of the XML data + * @throws Exception + */ + private byte[] serializeData() throws Exception { + final XmlSerializer out = new FastXmlSerializer(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + out.setOutput(outputStream, StandardCharsets.UTF_8.name()); + mSoftApStoreData.serializeData(out, mock(WifiConfigStoreEncryptionUtil.class)); + out.flush(); + return outputStream.toByteArray(); + } + + /** + * Helper function for parsing configuration data from a XML block. + * + * @param data XML data to parse from + * @throws Exception + */ + private void deserializeData(byte[] data) throws Exception { + final XmlPullParser in = Xml.newPullParser(); + final ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + in.setInput(inputStream, StandardCharsets.UTF_8.name()); + mSoftApStoreData.deserializeData(in, in.getDepth(), + WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION, + mock(WifiConfigStoreEncryptionUtil.class)); + } + + /** + * Verify that parsing an empty data doesn't cause any crash and no configuration should + * be deserialized. + * + * @throws Exception + */ + @Test + public void deserializeEmptyStoreData() throws Exception { + deserializeData(new byte[0]); + verify(mDataSource, never()).fromDeserialized(any()); + } + + /** + * Verify that SoftApStoreData is written to + * {@link WifiConfigStore#STORE_FILE_SHARED_SOFTAP}. + * + * @throws Exception + */ + @Test + public void getUserStoreFileId() throws Exception { + assertEquals(WifiConfigStore.STORE_FILE_SHARED_SOFTAP, + mSoftApStoreData.getStoreFileId()); + } + + /** + * Verify that the store data is serialized correctly, matches the predefined test XML data. + * + * @throws Exception + */ + @Test + public void serializeSoftAp() throws Exception { + WifiConfiguration softApConfig = new WifiConfiguration(); + softApConfig.SSID = TEST_SSID; + softApConfig.preSharedKey = TEST_PRESHARED_KEY; + softApConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + softApConfig.apBand = TEST_BAND; + softApConfig.apChannel = TEST_CHANNEL; + + when(mDataSource.toSerialize()).thenReturn(softApConfig); + byte[] actualData = serializeData(); + assertEquals(TEST_SOFTAP_CONFIG_XML_STRING, new String(actualData)); + } + + /** + * Verify that the store data is deserialized correctly using the predefined test XML data. + * + * @throws Exception + */ + @Test + public void deserializeSoftAp() throws Exception { + deserializeData(TEST_SOFTAP_CONFIG_XML_STRING.getBytes()); + + ArgumentCaptor<WifiConfiguration> softapConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); + verify(mDataSource).fromDeserialized(softapConfigCaptor.capture()); + WifiConfiguration softApConfig = softapConfigCaptor.getValue(); + assertNotNull(softApConfig); + assertEquals(softApConfig.SSID, TEST_SSID); + assertEquals(softApConfig.preSharedKey, TEST_PRESHARED_KEY); + assertTrue(softApConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)); + assertEquals(softApConfig.hiddenSSID, TEST_HIDDEN); + assertEquals(softApConfig.apBand, TEST_BAND); + assertEquals(softApConfig.apChannel, TEST_CHANNEL); + } + + /** + * Verify that the store data is serialized/deserialized correctly. + * + * @throws Exception + */ + @Test + public void serializeDeserializeSoftAp() throws Exception { + WifiConfiguration softApConfig = new WifiConfiguration(); + softApConfig.SSID = TEST_SSID; + softApConfig.preSharedKey = TEST_PRESHARED_KEY; + softApConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + softApConfig.apBand = TEST_BAND; + softApConfig.apChannel = TEST_CHANNEL; + + // Serialize first. + when(mDataSource.toSerialize()).thenReturn(softApConfig); + byte[] serializedData = serializeData(); + + // Now deserialize first. + deserializeData(serializedData); + ArgumentCaptor<WifiConfiguration> softapConfigCaptor = + ArgumentCaptor.forClass(WifiConfiguration.class); + verify(mDataSource).fromDeserialized(softapConfigCaptor.capture()); + WifiConfiguration softApConfigDeserialized = softapConfigCaptor.getValue(); + assertNotNull(softApConfigDeserialized); + + assertEquals(softApConfig.SSID, softApConfigDeserialized.SSID); + assertEquals(softApConfig.preSharedKey, softApConfigDeserialized.preSharedKey); + assertEquals(softApConfig.allowedKeyManagement, + softApConfigDeserialized.allowedKeyManagement); + assertEquals(softApConfig.hiddenSSID, softApConfigDeserialized.hiddenSSID); + assertEquals(softApConfig.apBand, softApConfigDeserialized.apBand); + assertEquals(softApConfig.apChannel, softApConfigDeserialized.apChannel); + } +} diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index f150bf797..71b034a2e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -21,9 +21,11 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,15 +48,17 @@ import androidx.test.filters.SmallTest; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.wifi.R; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; import java.io.File; -import java.lang.reflect.Method; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; @@ -89,30 +93,32 @@ public class WifiApConfigStoreTest extends WifiBaseTest { @Mock private Context mContext; @Mock private WifiInjector mWifiInjector; + private TestLooper mLooper; private Handler mHandler; @Mock private BackupManagerProxy mBackupManagerProxy; @Mock private FrameworkFacade mFrameworkFacade; - private File mApConfigFile; + @Mock private WifiConfigStore mWifiConfigStore; + @Mock private WifiConfigManager mWifiConfigManager; + private File mLegacyApConfigFile; private Random mRandom; private MockResources mResources; @Mock private ApplicationInfo mMockApplInfo; private BroadcastReceiver mBroadcastReceiver; @Mock private NotificationManager mNotificationManager; @Mock private MacAddressUtil mMacAddressUtil; + private SoftApStoreData.DataSource mDataStoreSource; private ArrayList<Integer> mKnownGood2GChannelList; @Before public void setUp() throws Exception { - mHandler = new Handler(new TestLooper().getLooper()); + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)) .thenReturn(mNotificationManager); mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.P; when(mContext.getApplicationInfo()).thenReturn(mMockApplInfo); - /* Create a temporary file for AP config file storage. */ - mApConfigFile = File.createTempFile(TEST_AP_CONFIG_FILE_PREFIX, ""); - /* Setup expectations for Resources to return some default settings. */ mResources = new MockResources(); mResources.setString(R.string.config_wifi_framework_sap_2G_channel_list, @@ -139,28 +145,39 @@ public class WifiApConfigStoreTest extends WifiBaseTest { when(mMacAddressUtil.calculatePersistentMac(any(), any())).thenReturn(TEST_RANDOMIZED_MAC); } - @After - public void cleanUp() { - /* Remove the temporary AP config file. */ - mApConfigFile.delete(); - } - /** * Helper method to create and verify actions for the ApConfigStore used in the following tests. */ - private WifiApConfigStore createWifiApConfigStore() { - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + private WifiApConfigStore createWifiApConfigStore(String legacyFilePath) { + WifiApConfigStore store; + if (legacyFilePath == null) { + store = new WifiApConfigStore( + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, + mWifiConfigStore, mWifiConfigManager); + } else { + store = new WifiApConfigStore( + mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, + mWifiConfigStore, mWifiConfigManager, legacyFilePath); + } ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); mBroadcastReceiver = broadcastReceiverCaptor.getValue(); + verify(mWifiConfigStore).registerStoreData(any()); + ArgumentCaptor<SoftApStoreData.DataSource> dataStoreSourceArgumentCaptor = + ArgumentCaptor.forClass(SoftApStoreData.DataSource.class); + verify(mWifiInjector).makeSoftApStoreData(dataStoreSourceArgumentCaptor.capture()); + mDataStoreSource = dataStoreSourceArgumentCaptor.getValue(); + return store; } + private WifiApConfigStore createWifiApConfigStore() { + return createWifiApConfigStore(null); + } + /** * Generate a WifiConfiguration based on the specified parameters. */ @@ -177,16 +194,28 @@ public class WifiApConfigStoreTest extends WifiBaseTest { return config; } - private void writeApConfigFile(WifiConfiguration config) throws Exception { - Method m = WifiApConfigStore.class.getDeclaredMethod( - "writeApConfiguration", String.class, WifiConfiguration.class); - m.setAccessible(true); - m.invoke(null, mApConfigFile.getPath(), config); + private void writeLegacyApConfigFile(WifiConfiguration config) throws Exception { + try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream( + new FileOutputStream(mLegacyApConfigFile)))) { + out.writeInt(WifiApConfigStore.AP_CONFIG_FILE_VERSION); + out.writeUTF(config.SSID); + out.writeInt(config.apBand); + out.writeInt(config.apChannel); + out.writeBoolean(config.hiddenSSID); + int authType = config.getAuthType(); + out.writeInt(authType); + if (authType != KeyMgmt.NONE) { + out.writeUTF(config.preSharedKey); + } + } catch (IOException e) { + fail("Error writing hotspot configuration" + e); + } } private void verifyApConfig(WifiConfiguration config1, WifiConfiguration config2) { assertEquals(config1.SSID, config2.SSID); assertEquals(config1.preSharedKey, config2.preSharedKey); + assertEquals(config1.allowedKeyManagement, config2.allowedKeyManagement); assertEquals(config1.getAuthType(), config2.getAuthType()); assertEquals(config1.apBand, config2.apBand); assertEquals(config1.apChannel, config2.apChannel); @@ -224,18 +253,17 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void initWithDefaultConfiguration() throws Exception { - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); } /** * Verify WifiApConfigStore can correctly load the existing configuration - * from the config file. + * from the legacy config file and migrate it to the new config store. */ @Test - public void initWithExistingConfiguration() throws Exception { + public void initWithExistingConfigurationInLegacyFile() throws Exception { WifiConfiguration expectedConfig = setupApConfig( "ConfiguredAP", /* SSID */ "randomKey", /* preshared key */ @@ -243,11 +271,25 @@ public class WifiApConfigStoreTest extends WifiBaseTest { 1, /* AP band (5GHz) */ 40, /* AP channel */ true /* Hidden SSID */); - writeApConfigFile(expectedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + /* Create a temporary file for AP config file storage. */ + mLegacyApConfigFile = File.createTempFile(TEST_AP_CONFIG_FILE_PREFIX, ""); + + writeLegacyApConfigFile(expectedConfig); + WifiApConfigStore store = createWifiApConfigStore(mLegacyApConfigFile.getPath()); + verify(mWifiConfigManager).saveToStore(true); + verify(mBackupManagerProxy).notifyDataChanged(); verifyApConfig(expectedConfig, store.getApConfiguration()); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + // Simulate the config store read to trigger the write to new config store. + mDataStoreSource.reset(); + mLooper.dispatchAll(); + // Triggers write twice: + // a) On reading the legacy file (new config store not ready yet) + // b) When the new config store is ready. + verify(mWifiConfigManager, times(2)).saveToStore(true); + + // The temporary legacy AP config file should be removed after migration. + assertFalse(mLegacyApConfigFile.exists()); } /** @@ -265,14 +307,14 @@ public class WifiApConfigStoreTest extends WifiBaseTest { 1, /* AP band (5GHz) */ 40, /* AP channel */ true /* Hidden SSID */); - writeApConfigFile(expectedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + mDataStoreSource.fromDeserialized(expectedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); store.setApConfiguration(null); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verifyDefaultApConfig(mDataStoreSource.toSerialize(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); verify(mBackupManagerProxy).notifyDataChanged(); } @@ -282,10 +324,10 @@ public class WifiApConfigStoreTest extends WifiBaseTest { @Test public void updateApConfiguration() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); /* Update with a valid configuration. */ WifiConfiguration expectedConfig = setupApConfig( @@ -297,7 +339,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { true /* Hidden SSID */); store.setApConfiguration(expectedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); - verify(mBackupManagerProxy).notifyDataChanged(); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager, times(2)).saveToStore(true); + verify(mBackupManagerProxy, times(2)).notifyDataChanged(); } /** @@ -308,10 +352,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { @Test public void convertSingleModeDeviceAnyTo5Ghz() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); /* Update with a valid configuration. */ WifiConfiguration providedConfig = setupApConfig( @@ -331,7 +374,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { false /* Hidden SSID */); store.setApConfiguration(providedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); - verify(mBackupManagerProxy).notifyDataChanged(); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager, times(2)).saveToStore(true); + verify(mBackupManagerProxy, times(2)).notifyDataChanged(); } /** @@ -342,10 +387,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { @Test public void singleModeDevice5GhzNotConverted() throws Exception { /* Initialize WifiApConfigStore with default configuration. */ - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); /* Update with a valid configuration. */ WifiConfiguration expectedConfig = setupApConfig( @@ -357,6 +401,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { false /* Hidden SSID */); store.setApConfiguration(expectedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager, times(2)).saveToStore(true); + verify(mBackupManagerProxy, times(2)).notifyDataChanged(); } /** @@ -369,10 +416,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_convert_apband_5ghz_to_any, true); /* Initialize WifiApConfigStore with default configuration. */ - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); /* Update with a valid configuration. */ WifiConfiguration providedConfig = setupApConfig( @@ -392,7 +438,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { false /* Hidden SSID */); store.setApConfiguration(providedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); - verify(mBackupManagerProxy).notifyDataChanged(); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager, times(2)).saveToStore(true); + verify(mBackupManagerProxy, times(2)).notifyDataChanged(); } /** @@ -405,10 +453,9 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_convert_apband_5ghz_to_any, true); /* Initialize WifiApConfigStore with default configuration. */ - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID); + verify(mWifiConfigManager).saveToStore(true); /* Update with a valid configuration. */ WifiConfiguration expectedConfig = setupApConfig( @@ -419,8 +466,10 @@ public class WifiApConfigStoreTest extends WifiBaseTest { 40, /* AP channel */ false /* Hidden SSID */); store.setApConfiguration(expectedConfig); - verify(mBackupManagerProxy).notifyDataChanged(); verifyApConfig(expectedConfig, store.getApConfiguration()); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager, times(2)).saveToStore(true); + verify(mBackupManagerProxy, times(2)).notifyDataChanged(); } /** @@ -445,11 +494,11 @@ public class WifiApConfigStoreTest extends WifiBaseTest { WifiConfiguration.AP_BAND_5GHZ, /* AP band */ WifiApConfigStore.AP_CHANNEL_DEFAULT, /* AP channel */ false /* Hidden SSID */); - writeApConfigFile(persistedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + mDataStoreSource.fromDeserialized(persistedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager).saveToStore(true); verify(mBackupManagerProxy).notifyDataChanged(); } @@ -468,11 +517,10 @@ public class WifiApConfigStoreTest extends WifiBaseTest { 40, /* AP channel */ false /* Hidden SSID */); - writeApConfigFile(persistedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + mDataStoreSource.fromDeserialized(persistedConfig); verifyApConfig(persistedConfig, store.getApConfiguration()); + verify(mWifiConfigManager, never()).saveToStore(true); verify(mBackupManagerProxy, never()).notifyDataChanged(); } @@ -500,11 +548,11 @@ public class WifiApConfigStoreTest extends WifiBaseTest { WifiApConfigStore.AP_CHANNEL_DEFAULT, /* AP channel */ false /* Hidden SSID */); - writeApConfigFile(persistedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + mDataStoreSource.fromDeserialized(persistedConfig); verifyApConfig(expectedConfig, store.getApConfiguration()); + verifyApConfig(expectedConfig, mDataStoreSource.toSerialize()); + verify(mWifiConfigManager).saveToStore(true); verify(mBackupManagerProxy).notifyDataChanged(); } @@ -525,11 +573,10 @@ public class WifiApConfigStoreTest extends WifiBaseTest { 40, /* AP channel */ false /* Hidden SSID */); - writeApConfigFile(persistedConfig); - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); + mDataStoreSource.fromDeserialized(persistedConfig); verifyApConfig(persistedConfig, store.getApConfiguration()); + verify(mWifiConfigManager, never()).saveToStore(true); verify(mBackupManagerProxy, never()).notifyDataChanged(); } @@ -538,9 +585,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void getDefaultApConfigurationIsValid() { - WifiApConfigStore store = new WifiApConfigStore( - mContext, mWifiInjector, mHandler, mBackupManagerProxy, mFrameworkFacade, - mApConfigFile.getPath()); + WifiApConfigStore store = createWifiApConfigStore(); WifiConfiguration config = store.getApConfiguration(); assertTrue(WifiApConfigStore.validateApWifiConfiguration(config)); } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index d48966143..edbd5cb7e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -213,7 +213,6 @@ public class WifiConfigManagerTest extends WifiBaseTest { .updateNetworkKeys(any(WifiConfiguration.class), any())) .thenReturn(true); - when(mWifiConfigStore.areStoresPresent()).thenReturn(true); setupStoreDataForRead(new ArrayList<>(), new ArrayList<>(), new HashMap<>()); when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); @@ -3758,8 +3757,6 @@ public class WifiConfigManagerTest extends WifiBaseTest { */ @Test public void testFreshInstallLoadFromStore() throws Exception { - when(mWifiConfigStore.areStoresPresent()).thenReturn(false); - assertTrue(mWifiConfigManager.loadFromStore()); verify(mWifiConfigStore).read(); @@ -3774,8 +3771,6 @@ public class WifiConfigManagerTest extends WifiBaseTest { */ @Test public void testFreshInstallLoadFromStoreAfterUserUnlock() throws Exception { - when(mWifiConfigStore.areStoresPresent()).thenReturn(false); - int user1 = TEST_DEFAULT_USER; // Unlock the user1 (default user) for the first time and ensure that we don't read the @@ -3797,7 +3792,6 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testHandleUserSwitchAfterFreshInstall() throws Exception { int user2 = TEST_DEFAULT_USER + 1; - when(mWifiConfigStore.areStoresPresent()).thenReturn(false); assertTrue(mWifiConfigManager.loadFromStore()); verify(mWifiConfigStore).read(); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java index 06a246593..814dc6134 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java @@ -51,6 +51,7 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -212,7 +213,7 @@ public class WifiConfigStoreTest extends WifiBaseTest { setupMocks(); mWifiConfigStore = new WifiConfigStore(mContext, new Handler(mLooper.getLooper()), mClock, - mWifiMetrics, mSharedStore); + mWifiMetrics, Arrays.asList(mSharedStore)); // Enable verbose logging before tests. mWifiConfigStore.enableVerboseLogging(true); } @@ -420,7 +421,6 @@ public class WifiConfigStoreTest extends WifiBaseTest { // |readRawData| would return null. mWifiConfigStore.registerStoreData(sharedStoreData); mWifiConfigStore.registerStoreData(userStoreData); - assertFalse(mWifiConfigStore.areStoresPresent()); mWifiConfigStore.read(); // Ensure that we got the call to deserialize empty shared data, but no user data. @@ -450,7 +450,6 @@ public class WifiConfigStoreTest extends WifiBaseTest { mWifiConfigStore.registerStoreData(userStoreData); // Read both share and user config store. mWifiConfigStore.setUserStores(mUserStores); - assertFalse(mWifiConfigStore.areStoresPresent()); mWifiConfigStore.read(); // Ensure that we got the call to deserialize empty shared & user data. |