blob: 64e762bec88852cb3e3c05175ce68bda1da3991f [file] [log] [blame]
/*
* Copyright (C) 2016 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.*;
import static org.mockito.Mockito.*;
import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.MacAddress;
import android.net.wifi.WifiConfiguration;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.util.ArrayUtils;
import com.android.server.wifi.WifiConfigStore.StoreData;
import com.android.server.wifi.WifiConfigStore.StoreFile;
import com.android.server.wifi.util.XmlUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
*/
@SmallTest
public class WifiConfigStoreTest {
private static final String TEST_USER_DATA = "UserData";
private static final String TEST_SHARE_DATA = "ShareData";
private static final String TEST_CREATOR_NAME = "CreatorName";
private static final MacAddress TEST_RANDOMIZED_MAC =
MacAddress.fromString("da:a1:19:c4:26:fa");
private static final String TEST_DATA_XML_STRING_FORMAT =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<NetworkList>\n"
+ "<Network>\n"
+ "<WifiConfiguration>\n"
+ "<string name=\"ConfigKey\">%s</string>\n"
+ "<string name=\"SSID\">%s</string>\n"
+ "<null name=\"BSSID\" />\n"
+ "<null name=\"PreSharedKey\" />\n"
+ "<null name=\"WEPKeys\" />\n"
+ "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+ "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+ "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+ "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+ "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n"
+ "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n"
+ "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
+ "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n"
+ "<byte-array name=\"AllowedGroupMgmtCiphers\" num=\"0\"></byte-array>\n"
+ "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
+ "<boolean name=\"Shared\" value=\"%s\" />\n"
+ "<int name=\"Status\" value=\"2\" />\n"
+ "<null name=\"FQDN\" />\n"
+ "<null name=\"ProviderFriendlyName\" />\n"
+ "<null name=\"LinkedNetworksList\" />\n"
+ "<null name=\"DefaultGwMacAddress\" />\n"
+ "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n"
+ "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n"
+ "<int name=\"UserApproved\" value=\"0\" />\n"
+ "<boolean name=\"MeteredHint\" value=\"false\" />\n"
+ "<int name=\"MeteredOverride\" value=\"0\" />\n"
+ "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
+ "<int name=\"NumAssociation\" value=\"0\" />\n"
+ "<int name=\"CreatorUid\" value=\"%d\" />\n"
+ "<string name=\"CreatorName\">%s</string>\n"
+ "<null name=\"CreationTime\" />\n"
+ "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
+ "<null name=\"LastUpdateName\" />\n"
+ "<int name=\"LastConnectUid\" value=\"0\" />\n"
+ "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n"
+ "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n"
+ "<string name=\"RandomizedMacAddress\">%s</string>\n"
+ "<int name=\"MacRandomizationSetting\" value=\"1\" />\n"
+ "</WifiConfiguration>\n"
+ "<NetworkStatus>\n"
+ "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
+ "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n"
+ "<null name=\"ConnectChoice\" />\n"
+ "<long name=\"ConnectChoiceTimeStamp\" value=\"-1\" />\n"
+ "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
+ "</NetworkStatus>\n"
+ "<IpConfiguration>\n"
+ "<string name=\"IpAssignment\">DHCP</string>\n"
+ "<string name=\"ProxySettings\">NONE</string>\n"
+ "</IpConfiguration>\n"
+ "</Network>\n"
+ "</NetworkList>\n"
+ "<DeletedEphemeralSSIDList>\n"
+ "<map name=\"SSIDList\">\n"
+ "<long name=\"%s\" value=\"0\" />\n"
+ "</map>\n"
+ "</DeletedEphemeralSSIDList>\n"
+ "</WifiConfigStoreData>\n";
private static final String TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<%s/>n"
+ "</WifiConfigStoreData>\n";
private static final String TEST_DATA_XML_STRING_FORMAT_WITH_TWO_DATA_SOURCE =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<%s/>n"
+ "<%s/>n"
+ "</WifiConfigStoreData>\n";
// Test mocks
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
private TestAlarmManager mAlarmManager;
private TestLooper mLooper;
@Mock private Clock mClock;
@Mock private WifiMetrics mWifiMetrics;
private MockStoreFile mSharedStore;
private MockStoreFile mUserStore;
private MockStoreFile mUserNetworkSuggestionsStore;
private List<StoreFile> mUserStores = new ArrayList<StoreFile>();
private MockStoreData mSharedStoreData;
private MockStoreData mUserStoreData;
/**
* Test instance of WifiConfigStore.
*/
private WifiConfigStore mWifiConfigStore;
/**
* Setup mocks before the test starts.
*/
private void setupMocks() throws Exception {
MockitoAnnotations.initMocks(this);
mAlarmManager = new TestAlarmManager();
mLooper = new TestLooper();
when(mContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
mSharedStore = new MockStoreFile(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
mUserStore = new MockStoreFile(WifiConfigStore.STORE_FILE_USER_GENERAL);
mUserNetworkSuggestionsStore =
new MockStoreFile(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS);
mUserStores.add(mUserStore);
mUserStores.add(mUserNetworkSuggestionsStore);
mSharedStoreData = new MockStoreData(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
mUserStoreData = new MockStoreData(WifiConfigStore.STORE_FILE_USER_GENERAL);
}
/**
* Setup the test environment.
*/
@Before
public void setUp() throws Exception {
setupMocks();
mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mWifiMetrics,
mSharedStore);
// Enable verbose logging before tests.
mWifiConfigStore.enableVerboseLogging(true);
}
/**
* Called after each test
*/
@After
public void cleanup() {
validateMockitoUsage();
}
/**
* Verify that no write occurs if there is {@link StoreData} registered for any
* {@link StoreFile}.
*
* @throws Exception
*/
@Test
public void testWriteWithNoStoreData() throws Exception {
// Perform force write to both share and user store file.
mWifiConfigStore.setUserStores(mUserStores);
mWifiConfigStore.write(true);
assertFalse(mSharedStore.isStoreWritten());
assertFalse(mUserStore.isStoreWritten());
assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
verify(mWifiMetrics, never()).noteWifiConfigStoreWriteDuration(anyInt());
}
/**
* Tests the write API with the force flag set to true.
* Expected behavior: This should trigger an immediate write to the store files and no alarms
* should be started.
*/
@Test
public void testForceWrite() throws Exception {
// Register data container.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
mWifiConfigStore.write(true);
assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertTrue(mSharedStore.isStoreWritten());
assertTrue(mUserStore.isStoreWritten());
assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
}
/**
* Tests the write API with the force flag set to false.
* Expected behavior: This should set an alarm to write to the store files.
*/
@Test
public void testBufferedWrite() throws Exception {
// Register data container.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
mWifiConfigStore.write(false);
assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertFalse(mSharedStore.isStoreWritten());
assertFalse(mUserStore.isStoreWritten());
assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
// Now send the alarm and ensure that the writes happen.
mAlarmManager.dispatch(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG);
mLooper.dispatchAll();
assertTrue(mSharedStore.isStoreWritten());
assertTrue(mUserStore.isStoreWritten());
assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
}
/**
* Tests the force write after a buffered write.
* Expected behaviour: The force write should override the previous buffered write and stop the
* buffer write alarms.
*/
@Test
public void testForceWriteAfterBufferedWrite() throws Exception {
// Register a test data container with bogus data.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
mSharedStoreData.setData("abcds");
mUserStoreData.setData("asdfa");
// Perform buffered write for both user and share store file.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
mWifiConfigStore.write(false);
assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertFalse(mSharedStore.isStoreWritten());
assertFalse(mUserStore.isStoreWritten());
// Update the container with new set of data. The send a force write and ensure that the
// writes have been performed and alarms have been stopped and updated data are written.
mUserStoreData.setData(TEST_USER_DATA);
mSharedStoreData.setData(TEST_SHARE_DATA);
mWifiConfigStore.write(true);
assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertTrue(mSharedStore.isStoreWritten());
assertTrue(mUserStore.isStoreWritten());
// Verify correct data are loaded to the data container after a read.
mWifiConfigStore.read();
assertEquals(TEST_USER_DATA, mUserStoreData.getData());
assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
}
/**
* Tests the force write with no new data after a buffered write.
* Expected behaviour: The force write should flush the previous buffered write and stop the
* buffer write alarms.
*/
@Test
public void testForceWriteWithNoNewDataAfterBufferedWrite() throws Exception {
// Register a test data container with bogus data.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
mSharedStoreData.setData("abcds");
mUserStoreData.setData("asdfa");
// Perform buffered write for both user and share store file.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
mWifiConfigStore.write(false);
assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertFalse(mSharedStore.isStoreWritten());
assertFalse(mUserStore.isStoreWritten());
// Containers have no new data.
mUserStoreData.setHasAnyNewData(false);
mSharedStoreData.setHasAnyNewData(false);
mWifiConfigStore.write(true);
assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
assertTrue(mSharedStore.isStoreWritten());
assertTrue(mUserStore.isStoreWritten());
// Verify correct data are loaded to the data container after a read.
mWifiConfigStore.read();
assertEquals("abcds", mSharedStoreData.getData());
assertEquals("asdfa", mUserStoreData.getData());
}
/**
* Tests the read API behaviour after a write to the store files.
* Expected behaviour: The read should return the same data that was last written.
*/
@Test
public void testReadAfterWrite() throws Exception {
// Register data container.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
// Read both share and user config store.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
// Verify no data is read.
assertNull(mUserStoreData.getData());
assertNull(mSharedStoreData.getData());
// Write share and user data.
mUserStoreData.setData(TEST_USER_DATA);
mSharedStoreData.setData(TEST_SHARE_DATA);
mWifiConfigStore.write(true);
// Read and verify the data content in the data container.
mWifiConfigStore.read();
assertEquals(TEST_USER_DATA, mUserStoreData.getData());
assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
verify(mWifiMetrics, times(2)).noteWifiConfigStoreReadDuration(anyInt());
verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
}
/**
* Tests the read API behaviour when the shared store file is empty and the user store
* is not yet visible (user not yet unlocked).
* Expected behaviour: The read should return an empty store data instance when the file not
* found exception is raised.
*/
@Test
public void testReadWithNoSharedStoreFileAndUserStoreNotVisible() throws Exception {
StoreData sharedStoreData = mock(StoreData.class);
when(sharedStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
StoreData userStoreData = mock(StoreData.class);
when(userStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
// Reading the mock store without a write should simulate the file not found case because
// |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.
verify(sharedStoreData).resetData();
verify(sharedStoreData).deserializeData(eq(null), anyInt());
verify(userStoreData, never()).resetData();
verify(userStoreData, never()).deserializeData(any(), anyInt());
}
/**
* Tests the read API behaviour when there are no user/shared store files on the device.
* Expected behaviour: The read should return an empty store data instance when the file not
* found exception is raised.
*/
@Test
public void testReadWithNoStoreFiles() throws Exception {
StoreData sharedStoreData = mock(StoreData.class);
when(sharedStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
StoreData userStoreData = mock(StoreData.class);
when(userStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
// Reading the mock store without a write should simulate the file not found case because
// |readRawData| would return null.
mWifiConfigStore.registerStoreData(sharedStoreData);
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.
verify(userStoreData).resetData();
verify(userStoreData).deserializeData(eq(null), anyInt());
verify(sharedStoreData).resetData();
verify(sharedStoreData).deserializeData(eq(null), anyInt());
}
/**
* Tests the read API behaviour after a write to the shared store file when the user
* store file is null.
* Expected behaviour: The read should return the same data that was last written.
*/
@Test
public void testReadAfterWriteWithNoUserStore() throws Exception {
// Setup data container.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mSharedStoreData.setData(TEST_SHARE_DATA);
// Perform write for the share store file.
mWifiConfigStore.write(true);
mWifiConfigStore.read();
// Verify data content for both user and share data.
assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
}
/**
* Verifies that a read operation will reset the data in the data container, to avoid
* any stale data from previous read.
*
* @throws Exception
*/
@Test
public void testReadWillResetStoreData() throws Exception {
// Register and setup store data.
mWifiConfigStore.registerStoreData(mSharedStoreData);
mWifiConfigStore.registerStoreData(mUserStoreData);
// Perform force write with empty data content to both user and share store file.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
mWifiConfigStore.write(true);
// Setup data container with some value.
mUserStoreData.setData(TEST_USER_DATA);
mSharedStoreData.setData(TEST_SHARE_DATA);
// Perform read of both user and share store file and verify data in the data container
// is in sync (empty) with what is in the file.
mWifiConfigStore.read();
assertNull(mSharedStoreData.getData());
assertNull(mUserStoreData.getData());
}
/**
* Verify that a store file contained WiFi configuration store data (network list and
* deleted ephemeral SSID list) using the predefined test XML data is read and parsed
* correctly.
*
* @throws Exception
*/
@Test
public void testReadWifiConfigStoreData() throws Exception {
// Setup network list.
NetworkListStoreData networkList = new NetworkListUserStoreData(mContext);
mWifiConfigStore.registerStoreData(networkList);
WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
openNetwork.creatorName = TEST_CREATOR_NAME;
openNetwork.setIpConfiguration(
WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
openNetwork.setRandomizedMacAddress(TEST_RANDOMIZED_MAC);
List<WifiConfiguration> userConfigs = new ArrayList<>();
userConfigs.add(openNetwork);
// Setup deleted ephemeral SSID list.
DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
new DeletedEphemeralSsidsStoreData(mClock);
mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
String testSsid = "\"Test SSID\"";
Map<String, Long> ssidMap = new HashMap<>();
ssidMap.put(testSsid, 0L);
// Setup user store XML bytes.
String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
openNetwork.configKey().replaceAll("\"", "&quot;"),
openNetwork.SSID.replaceAll("\"", "&quot;"),
openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
openNetwork.getRandomizedMacAddress(), testSsid.replaceAll("\"", "&quot;"));
byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
mUserStore.storeRawDataToWrite(xmlBytes);
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
userConfigs, networkList.getConfigurations());
assertEquals(ssidMap, deletedEphemeralSsids.getSsidToTimeMap());
}
/**
* Verify that the WiFi configuration store data containing network list and deleted
* ephemeral SSID list are serialized correctly, matches the predefined test XML data.
*
* @throws Exception
*/
@Test
public void testWriteWifiConfigStoreData() throws Exception {
// Setup user store.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
// Setup network list store data.
NetworkListStoreData networkList = new NetworkListUserStoreData(mContext);
mWifiConfigStore.registerStoreData(networkList);
WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
openNetwork.creatorName = TEST_CREATOR_NAME;
openNetwork.setIpConfiguration(
WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
openNetwork.setRandomizedMacAddress(TEST_RANDOMIZED_MAC);
List<WifiConfiguration> userConfigs = new ArrayList<>();
userConfigs.add(openNetwork);
networkList.setConfigurations(userConfigs);
// Setup deleted ephemeral SSID list store data.
DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
new DeletedEphemeralSsidsStoreData(mClock);
mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
String testSsid = "Test SSID";
Map<String, Long> ssidMap = new HashMap<>();
ssidMap.put(testSsid, 0L);
deletedEphemeralSsids.setSsidToTimeMap(ssidMap);
// Setup expected XML bytes.
String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
openNetwork.configKey().replaceAll("\"", "&quot;"),
openNetwork.SSID.replaceAll("\"", "&quot;"),
openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
openNetwork.getRandomizedMacAddress(), testSsid.replaceAll("\"", "&quot;"));
mWifiConfigStore.write(true);
// Verify the user store content.
assertEquals(xmlString, new String(mUserStore.getStoreBytes()));
}
/**
* Verify that a store file contained WiFi configuration store data (network list and
* deleted ephemeral SSID list) using the predefined test XML data is read and parsed
* correctly.
*
* @throws Exception
*/
@Test
public void testReadWifiConfigStoreDataIndicateClientsThatThereIsNoDataForThem()
throws Exception {
// Set both the user store & shared store files.
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
String storeData1Name = "test1";
String storeData2Name = "test2";
StoreData storeData1 = mock(StoreData.class);
StoreData storeData2 = mock(StoreData.class);
assertTrue(mWifiConfigStore.registerStoreData(storeData1));
assertTrue(mWifiConfigStore.registerStoreData(storeData2));
String fileContentsXmlStringWithOnlyStoreData1 =
String.format(TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE, storeData1Name);
String fileContentsXmlStringWithOnlyStoreData2 =
String.format(TEST_DATA_XML_STRING_FORMAT_WITH_ONE_DATA_SOURCE, storeData2Name);
String fileContentsXmlStringWithStoreData1AndStoreData2 =
String.format(TEST_DATA_XML_STRING_FORMAT_WITH_TWO_DATA_SOURCE,
storeData1Name, storeData2Name);
// Scenario 1: StoreData1 in shared store file.
when(storeData1.getName()).thenReturn(storeData1Name);
when(storeData2.getName()).thenReturn(storeData2Name);
when(storeData1.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
when(storeData2.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
mSharedStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData1.getBytes());
mUserStore.storeRawDataToWrite(null);
mWifiConfigStore.read();
verify(storeData1).deserializeData(notNull(), anyInt());
verify(storeData1, never()).deserializeData(eq(null), anyInt());
verify(storeData2).deserializeData(eq(null), anyInt());
reset(storeData1, storeData2);
// Scenario 2: StoreData2 in user store file.
when(storeData1.getName()).thenReturn(storeData1Name);
when(storeData2.getName()).thenReturn(storeData2Name);
when(storeData1.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
when(storeData2.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
mSharedStore.storeRawDataToWrite(null);
mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
mWifiConfigStore.read();
verify(storeData1).deserializeData(eq(null), anyInt());
verify(storeData2).deserializeData(notNull(), anyInt());
verify(storeData2, never()).deserializeData(eq(null), anyInt());
reset(storeData1, storeData2);
// Scenario 3: StoreData1 in shared store file & StoreData2 in user store file.
when(storeData1.getName()).thenReturn(storeData1Name);
when(storeData2.getName()).thenReturn(storeData2Name);
when(storeData1.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
when(storeData2.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
mSharedStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData1.getBytes());
mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
mWifiConfigStore.read();
verify(storeData1).deserializeData(notNull(), anyInt());
verify(storeData1, never()).deserializeData(eq(null), anyInt());
verify(storeData2).deserializeData(notNull(), anyInt());
verify(storeData2, never()).deserializeData(eq(null), anyInt());
reset(storeData1, storeData2);
// Scenario 4: StoreData1 & StoreData2 in shared store file.
when(storeData1.getName()).thenReturn(storeData1Name);
when(storeData2.getName()).thenReturn(storeData2Name);
when(storeData1.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
when(storeData2.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
mSharedStore.storeRawDataToWrite(
fileContentsXmlStringWithStoreData1AndStoreData2.getBytes());
mUserStore.storeRawDataToWrite(null);
mWifiConfigStore.read();
verify(storeData1).deserializeData(notNull(), anyInt());
verify(storeData1, never()).deserializeData(eq(null), anyInt());
verify(storeData2).deserializeData(notNull(), anyInt());
verify(storeData2, never()).deserializeData(eq(null), anyInt());
reset(storeData1, storeData2);
}
/**
* Tests the write API behavior when all the store data's registered for a given store file
* has no new data to write.
* Expected behaviour: The write should not trigger a new file write for that specific store
* file.
*/
@Test
public void testWriteWithNoNewData() throws Exception {
StoreData sharedStoreData = mock(StoreData.class);
when(sharedStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
when(sharedStoreData.hasNewDataToSerialize()).thenReturn(true);
when(sharedStoreData.getName()).thenReturn("sharedStoreData");
StoreData userStoreData = mock(StoreData.class);
when(userStoreData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
when(userStoreData.hasNewDataToSerialize()).thenReturn(true);
when(userStoreData.getName()).thenReturn("userStoreData");
StoreData userStoreNetworkSuggestionsData =
mock(StoreData.class);
when(userStoreNetworkSuggestionsData.getStoreFileId())
.thenReturn(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS);
when(userStoreNetworkSuggestionsData.hasNewDataToSerialize()).thenReturn(false);
when(userStoreNetworkSuggestionsData.getName())
.thenReturn("userStoreNetworkSuggestionsData");
assertTrue(mWifiConfigStore.registerStoreData(sharedStoreData));
assertTrue(mWifiConfigStore.registerStoreData(userStoreData));
assertTrue(mWifiConfigStore.registerStoreData(userStoreNetworkSuggestionsData));
// Write both share and user config store.
mWifiConfigStore.setUserStores(mUserStores);
// Now trigger a write.
mWifiConfigStore.write(true);
verify(sharedStoreData).hasNewDataToSerialize();
verify(userStoreData).hasNewDataToSerialize();
verify(userStoreNetworkSuggestionsData).hasNewDataToSerialize();
// Verify that we serialized data from the first 2 data source, but not from the last one.
verify(sharedStoreData).serializeData(any());
verify(userStoreData).serializeData(any());
verify(userStoreNetworkSuggestionsData, never()).serializeData(any());
}
/**
* Verify that a XmlPullParserException will be thrown when reading an user store file
* containing unknown data.
*
* @throws Exception
*/
@Test(expected = XmlPullParserException.class)
public void testReadUserStoreContainedUnknownData() throws Exception {
String storeFileData =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<UnknownTag>\n" // No StoreData registered to handle this tag.
+ "</UnknownTag>\n"
+ "</WifiConfigStoreData>\n";
mUserStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
mWifiConfigStore.switchUserStoresAndRead(mUserStores);
}
/**
* Verify that a XmlPullParserException will be thrown when reading the share store file
* containing unknown data.
*
* @throws Exception
*/
@Test(expected = XmlPullParserException.class)
public void testReadShareStoreContainedUnknownData() throws Exception {
String storeFileData =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<WifiConfigStoreData>\n"
+ "<int name=\"Version\" value=\"1\" />\n"
+ "<UnknownTag>\n" // No StoreData registered to handle this tag.
+ "</UnknownTag>\n"
+ "</WifiConfigStoreData>\n";
mSharedStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
mWifiConfigStore.read();
}
/**
* Mock Store File to redirect all file writes from WifiConfigStore to local buffers.
* This can be used to examine the data output by WifiConfigStore.
*/
private class MockStoreFile extends StoreFile {
private byte[] mStoreBytes;
private boolean mStoreWritten;
MockStoreFile(@WifiConfigStore.StoreFileId int fileId) {
super(new File("MockStoreFile"), fileId);
}
@Override
public byte[] readRawData() {
return mStoreBytes;
}
@Override
public void storeRawDataToWrite(byte[] data) {
mStoreBytes = data;
mStoreWritten = false;
}
@Override
public boolean exists() {
return (mStoreBytes != null);
}
@Override
public void writeBufferedRawData() {
if (!ArrayUtils.isEmpty(mStoreBytes)) {
mStoreWritten = true;
}
}
public byte[] getStoreBytes() {
return mStoreBytes;
}
public boolean isStoreWritten() {
return mStoreWritten;
}
}
/**
* Mock data container for providing test data for the store file.
*/
private class MockStoreData implements StoreData {
private static final String XML_TAG_TEST_HEADER = "TestHeader";
private static final String XML_TAG_TEST_DATA = "TestData";
private @WifiConfigStore.StoreFileId int mFileId;
private String mData;
private boolean mHasAnyNewData = true;
MockStoreData(@WifiConfigStore.StoreFileId int fileId) {
mFileId = fileId;
}
@Override
public void serializeData(XmlSerializer out)
throws XmlPullParserException, IOException {
XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mData);
}
@Override
public void deserializeData(XmlPullParser in, int outerTagDepth)
throws XmlPullParserException, IOException {
if (in == null) {
return;
}
mData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA);
}
@Override
public void resetData() {
mData = null;
}
@Override
public boolean hasNewDataToSerialize() {
return mHasAnyNewData;
}
@Override
public String getName() {
return XML_TAG_TEST_HEADER;
}
@Override
public @WifiConfigStore.StoreFileId int getStoreFileId() {
return mFileId;
}
public String getData() {
return mData;
}
public void setData(String data) {
mData = data;
}
public void setHasAnyNewData(boolean hasAnyNewData) {
mHasAnyNewData = hasAnyNewData;
}
}
}