1 /*
2  * Copyright (C) 2016 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 static org.junit.Assert.*;
20 import static org.mockito.Mockito.*;
21 
22 import android.app.test.MockAnswerUtil;
23 import android.app.test.TestAlarmManager;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.net.MacAddress;
27 import android.net.wifi.WifiConfiguration;
28 import android.net.wifi.WifiMigration;
29 import android.net.wifi.util.HexEncoding;
30 import android.os.Handler;
31 import android.os.UserHandle;
32 import android.os.test.TestLooper;
33 
34 import androidx.test.filters.SmallTest;
35 
36 import com.android.dx.mockito.inline.extended.ExtendedMockito;
37 import com.android.server.wifi.WifiConfigStore.StoreData;
38 import com.android.server.wifi.WifiConfigStore.StoreFile;
39 import com.android.server.wifi.util.ArrayUtils;
40 import com.android.server.wifi.util.EncryptedData;
41 import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
42 import com.android.server.wifi.util.XmlUtil;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 import org.mockito.MockitoSession;
50 import org.mockito.stubbing.Answer;
51 import org.xmlpull.v1.XmlPullParser;
52 import org.xmlpull.v1.XmlPullParserException;
53 import org.xmlpull.v1.XmlSerializer;
54 
55 import java.io.File;
56 import java.io.IOException;
57 import java.io.InputStream;
58 import java.nio.charset.StandardCharsets;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.List;
62 import java.util.Random;
63 
64 /**
65  * Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
66  */
67 @SmallTest
68 public class WifiConfigStoreTest extends WifiBaseTest {
69     private static final String TEST_USER_DATA = "UserData";
70     private static final String TEST_SHARE_DATA = "ShareData";
71     private static final String TEST_CREATOR_NAME = "CreatorName";
72     private static final MacAddress TEST_RANDOMIZED_MAC =
73             MacAddress.fromString("da:a1:19:c4:26:fa");
74 
75     private static final String TEST_DATA_XML_STRING_FORMAT =
76             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
77                     + "<WifiConfigStoreData>\n"
78                     + "<int name=\"Version\" value=\"3\" />\n"
79                     + "<NetworkList>\n"
80                     + "<Network>\n"
81                     + "<WifiConfiguration>\n"
82                     + "<string name=\"ConfigKey\">%s</string>\n"
83                     + "<string name=\"SSID\">%s</string>\n"
84                     + "<null name=\"PreSharedKey\" />\n"
85                     + "<null name=\"WEPKeys\" />\n"
86                     + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
87                     + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
88                     + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
89                     + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
90                     + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n"
91                     + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n"
92                     + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
93                     + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n"
94                     + "<byte-array name=\"AllowedGroupMgmtCiphers\" num=\"0\"></byte-array>\n"
95                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
96                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
97                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
98                     + "<boolean name=\"Trusted\" value=\"true\" />\n"
99                     + "<null name=\"BSSID\" />\n"
100                     + "<int name=\"Status\" value=\"2\" />\n"
101                     + "<null name=\"FQDN\" />\n"
102                     + "<null name=\"ProviderFriendlyName\" />\n"
103                     + "<null name=\"LinkedNetworksList\" />\n"
104                     + "<null name=\"DefaultGwMacAddress\" />\n"
105                     + "<boolean name=\"ValidatedInternetAccess\" value=\"false\" />\n"
106                     + "<boolean name=\"NoInternetAccessExpected\" value=\"false\" />\n"
107                     + "<boolean name=\"MeteredHint\" value=\"false\" />\n"
108                     + "<int name=\"MeteredOverride\" value=\"0\" />\n"
109                     + "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
110                     + "<int name=\"CreatorUid\" value=\"%d\" />\n"
111                     + "<string name=\"CreatorName\">%s</string>\n"
112                     + "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
113                     + "<null name=\"LastUpdateName\" />\n"
114                     + "<int name=\"LastConnectUid\" value=\"0\" />\n"
115                     + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n"
116                     + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n"
117                     + "<string name=\"RandomizedMacAddress\">%s</string>\n"
118                     + "<int name=\"MacRandomizationSetting\" value=\"1\" />\n"
119                     + "<int name=\"CarrierId\" value=\"-1\" />\n"
120                     + "<boolean name=\"IsMostRecentlyConnected\" value=\"false\" />\n"
121                     + "</WifiConfiguration>\n"
122                     + "<NetworkStatus>\n"
123                     + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
124                     + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\n"
125                     + "<null name=\"ConnectChoice\" />\n"
126                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
127                     + "</NetworkStatus>\n"
128                     + "<IpConfiguration>\n"
129                     + "<string name=\"IpAssignment\">DHCP</string>\n"
130                     + "<string name=\"ProxySettings\">NONE</string>\n"
131                     + "</IpConfiguration>\n"
132                     + "</Network>\n"
133                     + "</NetworkList>\n"
134                     + "</WifiConfigStoreData>\n";
135 
136     private static final String TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE =
137             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
138                     + "<WifiConfigStoreData>\n"
139                     + "<int name=\"Version\" value=\"1\" />\n"
140                     + "<%s/>n"
141                     + "</WifiConfigStoreData>\n";
142     private static final String TEST_DATA_XML_STRING_FORMAT_V1_WITH_TWO_DATA_SOURCE =
143             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
144                     + "<WifiConfigStoreData>\n"
145                     + "<int name=\"Version\" value=\"1\" />\n"
146                     + "<%s/>n"
147                     + "<%s/>n"
148                     + "</WifiConfigStoreData>\n";
149     private static final String TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE =
150             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
151                     + "<WifiConfigStoreData>\n"
152                     + "<int name=\"Version\" value=\"2\" />\n"
153                     + "<Integrity>\n"
154                     + "<byte-array name=\"EncryptedData\" num=\"48\">%s</byte-array>\n"
155                     + "<byte-array name=\"IV\" num=\"12\">%s</byte-array>\n"
156                     + "</Integrity>\n"
157                     + "<%s />\n"
158                     + "</WifiConfigStoreData>\n";
159     private static final String TEST_DATA_XML_STRING_FORMAT_V3_WITH_ONE_DATA_SOURCE =
160             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
161                     + "<WifiConfigStoreData>\n"
162                     + "<int name=\"Version\" value=\"3\" />\n"
163                     + "<%s />\n"
164                     + "</WifiConfigStoreData>\n";
165     // Test mocks
166     @Mock private Context mContext;
167     @Mock private PackageManager mPackageManager;
168     private TestAlarmManager mAlarmManager;
169     private TestLooper mLooper;
170     @Mock private Clock mClock;
171     @Mock private WifiMetrics mWifiMetrics;
172     @Mock private WifiConfigStoreEncryptionUtil mEncryptionUtil;
173     private MockStoreFile mSharedStore;
174     private MockStoreFile mSharedSoftApStore;
175     private MockStoreFile mUserStore;
176     private MockStoreFile mUserNetworkSuggestionsStore;
177     private List<StoreFile> mUserStores = new ArrayList<StoreFile>();
178     private MockStoreData mSharedStoreData;
179     private MockStoreData mUserStoreData;
180     private MockitoSession mSession;
181 
182     /**
183      * Test instance of WifiConfigStore.
184      */
185     private WifiConfigStore mWifiConfigStore;
186 
187     /**
188      * Setup mocks before the test starts.
189      */
setupMocks()190     private void setupMocks() throws Exception {
191         MockitoAnnotations.initMocks(this);
192         mAlarmManager = new TestAlarmManager();
193         mLooper = new TestLooper();
194         when(mContext.getSystemService(Context.ALARM_SERVICE))
195                 .thenReturn(mAlarmManager.getAlarmManager());
196         when(mContext.getPackageManager()).thenReturn(mPackageManager);
197         when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
198         when(mEncryptionUtil.encrypt(any(byte[].class)))
199                 .thenReturn(new EncryptedData(new byte[0], new byte[0]));
200         when(mEncryptionUtil.decrypt(any(EncryptedData.class)))
201                 .thenReturn(new byte[0]);
202         mSharedStore = new MockStoreFile(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
203         mSharedSoftApStore = new MockStoreFile(WifiConfigStore.STORE_FILE_SHARED_SOFTAP);
204         mUserStore = new MockStoreFile(WifiConfigStore.STORE_FILE_USER_GENERAL);
205         mUserNetworkSuggestionsStore =
206                 new MockStoreFile(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS);
207         mUserStores.add(mUserStore);
208         mUserStores.add(mUserNetworkSuggestionsStore);
209 
210         mSharedStoreData = new MockStoreData(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
211         mUserStoreData = new MockStoreData(WifiConfigStore.STORE_FILE_USER_GENERAL);
212 
213         mSession = ExtendedMockito.mockitoSession()
214                 .mockStatic(WifiMigration.class, withSettings().lenient())
215                 .startMocking();
216         when(WifiMigration.convertAndRetrieveSharedConfigStoreFile(anyInt())).thenReturn(null);
217         when(WifiMigration.convertAndRetrieveUserConfigStoreFile(anyInt(), any())).thenReturn(null);
218     }
219 
220     /**
221      * Setup the test environment.
222      */
223     @Before
setUp()224     public void setUp() throws Exception {
225         setupMocks();
226 
227         mWifiConfigStore = new WifiConfigStore(mContext, new Handler(mLooper.getLooper()), mClock,
228                 mWifiMetrics, Arrays.asList(mSharedStore, mSharedSoftApStore));
229         // Enable verbose logging before tests.
230         mWifiConfigStore.enableVerboseLogging(true);
231     }
232 
233     /**
234      * Called after each test
235      */
236     @After
cleanup()237     public void cleanup() {
238         validateMockitoUsage();
239         if (mSession != null) {
240             mSession.finishMocking();
241         }
242     }
243 
244     /**
245      * Verify that no write occurs if there is {@link StoreData} registered for any
246      * {@link StoreFile}.
247      *
248      * @throws Exception
249      */
250     @Test
testWriteWithNoStoreData()251     public void testWriteWithNoStoreData() throws Exception {
252         // Perform force write to both share and user store file.
253         mWifiConfigStore.setUserStores(mUserStores);
254         mWifiConfigStore.write(true);
255 
256         assertFalse(mSharedStore.isStoreWritten());
257         assertFalse(mUserStore.isStoreWritten());
258         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
259 
260         verify(mWifiMetrics, never()).noteWifiConfigStoreWriteDuration(anyInt());
261     }
262 
263     /**
264      * Tests the write API with the force flag set to true.
265      * Expected behavior: This should trigger an immediate write to the store files and no alarms
266      * should be started.
267      */
268     @Test
testForceWrite()269     public void testForceWrite() throws Exception {
270         // Register data container.
271         mWifiConfigStore.registerStoreData(mSharedStoreData);
272         mWifiConfigStore.registerStoreData(mUserStoreData);
273 
274         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
275         mWifiConfigStore.write(true);
276 
277         assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
278         assertTrue(mSharedStore.isStoreWritten());
279         assertTrue(mUserStore.isStoreWritten());
280         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
281 
282         verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
283     }
284 
285     /**
286      * Tests the write API with the force flag set to false.
287      * Expected behavior: This should set an alarm to write to the store files.
288      */
289     @Test
testBufferedWrite()290     public void testBufferedWrite() throws Exception {
291         // Register data container.
292         mWifiConfigStore.registerStoreData(mSharedStoreData);
293         mWifiConfigStore.registerStoreData(mUserStoreData);
294 
295         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
296         mWifiConfigStore.write(false);
297 
298         assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
299         assertFalse(mSharedStore.isStoreWritten());
300         assertFalse(mUserStore.isStoreWritten());
301         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
302 
303         // Now send the alarm and ensure that the writes happen.
304         mAlarmManager.dispatch(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG);
305         mLooper.dispatchAll();
306         assertTrue(mSharedStore.isStoreWritten());
307         assertTrue(mUserStore.isStoreWritten());
308         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
309 
310         verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
311     }
312 
313     /**
314      * Tests the force write after a buffered write.
315      * Expected behaviour: The force write should override the previous buffered write and stop the
316      * buffer write alarms.
317      */
318     @Test
testForceWriteAfterBufferedWrite()319     public void testForceWriteAfterBufferedWrite() throws Exception {
320         // Register a test data container with bogus data.
321         mWifiConfigStore.registerStoreData(mSharedStoreData);
322         mWifiConfigStore.registerStoreData(mUserStoreData);
323 
324         mSharedStoreData.setData("abcds");
325         mUserStoreData.setData("asdfa");
326 
327         // Perform buffered write for both user and share store file.
328         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
329         mWifiConfigStore.write(false);
330 
331         assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
332         assertFalse(mSharedStore.isStoreWritten());
333         assertFalse(mUserStore.isStoreWritten());
334 
335         // Update the container with new set of data. The send a force write and ensure that the
336         // writes have been performed and alarms have been stopped and updated data are written.
337         mUserStoreData.setData(TEST_USER_DATA);
338         mSharedStoreData.setData(TEST_SHARE_DATA);
339         mWifiConfigStore.write(true);
340 
341         assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
342         assertTrue(mSharedStore.isStoreWritten());
343         assertTrue(mUserStore.isStoreWritten());
344 
345         // Verify correct data are loaded to the data container after a read.
346         mWifiConfigStore.read();
347         assertEquals(TEST_USER_DATA, mUserStoreData.getData());
348         assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
349     }
350 
351     /**
352      * Tests the force write with no new data after a buffered write.
353      * Expected behaviour: The force write should flush the previous buffered write and stop the
354      * buffer write alarms.
355      */
356     @Test
testForceWriteWithNoNewDataAfterBufferedWrite()357     public void testForceWriteWithNoNewDataAfterBufferedWrite() throws Exception {
358         // Register a test data container with bogus data.
359         mWifiConfigStore.registerStoreData(mSharedStoreData);
360         mWifiConfigStore.registerStoreData(mUserStoreData);
361 
362         mSharedStoreData.setData("abcds");
363         mUserStoreData.setData("asdfa");
364 
365         // Perform buffered write for both user and share store file.
366         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
367         mWifiConfigStore.write(false);
368 
369         assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
370         assertFalse(mSharedStore.isStoreWritten());
371         assertFalse(mUserStore.isStoreWritten());
372 
373         // Containers have no new data.
374         mUserStoreData.setHasAnyNewData(false);
375         mSharedStoreData.setHasAnyNewData(false);
376         mWifiConfigStore.write(true);
377 
378         assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
379         assertTrue(mSharedStore.isStoreWritten());
380         assertTrue(mUserStore.isStoreWritten());
381 
382         // Verify correct data are loaded to the data container after a read.
383         mWifiConfigStore.read();
384         assertEquals("abcds", mSharedStoreData.getData());
385         assertEquals("asdfa", mUserStoreData.getData());
386     }
387 
388 
389     /**
390      * Tests the read API behaviour after a write to the store files.
391      * Expected behaviour: The read should return the same data that was last written.
392      */
393     @Test
testReadAfterWrite()394     public void testReadAfterWrite() throws Exception {
395         // Register data container.
396         mWifiConfigStore.registerStoreData(mSharedStoreData);
397         mWifiConfigStore.registerStoreData(mUserStoreData);
398 
399         // Read both share and user config store.
400         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
401 
402         // Verify no data is read.
403         assertNull(mUserStoreData.getData());
404         assertNull(mSharedStoreData.getData());
405 
406         // Write share and user data.
407         mUserStoreData.setData(TEST_USER_DATA);
408         mSharedStoreData.setData(TEST_SHARE_DATA);
409         mWifiConfigStore.write(true);
410 
411         // Read and verify the data content in the data container.
412         mWifiConfigStore.read();
413         assertEquals(TEST_USER_DATA, mUserStoreData.getData());
414         assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
415 
416         verify(mWifiMetrics, times(2)).noteWifiConfigStoreReadDuration(anyInt());
417         verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
418     }
419 
420     /**
421      * Tests the read API behaviour when the shared store file is empty and the user store
422      * is not yet visible (user not yet unlocked).
423      * Expected behaviour: The read should return an empty store data instance when the file not
424      * found exception is raised.
425      */
426     @Test
testReadWithNoSharedStoreFileAndUserStoreNotVisible()427     public void testReadWithNoSharedStoreFileAndUserStoreNotVisible() throws Exception {
428         StoreData sharedStoreData = mock(StoreData.class);
429         when(sharedStoreData.getStoreFileId())
430                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
431         StoreData userStoreData = mock(StoreData.class);
432         when(userStoreData.getStoreFileId())
433                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
434 
435         // Reading the mock store without a write should simulate the file not found case because
436         // |readRawData| would return null.
437         mWifiConfigStore.registerStoreData(sharedStoreData);
438         mWifiConfigStore.registerStoreData(userStoreData);
439         mWifiConfigStore.read();
440 
441         // Ensure that we got the call to deserialize empty shared data, but no user data.
442         verify(sharedStoreData).resetData();
443         verify(sharedStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
444         verify(userStoreData, never()).resetData();
445         verify(userStoreData, never()).deserializeData(any(), anyInt(), anyInt(), any());
446     }
447 
448     /**
449      * Tests the read API behaviour when there are no user/shared store files on the device.
450      * Expected behaviour: The read should return an empty store data instance when the file not
451      * found exception is raised.
452      */
453     @Test
testReadWithNoStoreFiles()454     public void testReadWithNoStoreFiles() throws Exception {
455         StoreData sharedStoreData = mock(StoreData.class);
456         when(sharedStoreData.getStoreFileId())
457                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
458         StoreData userStoreData = mock(StoreData.class);
459         when(userStoreData.getStoreFileId())
460                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
461 
462         // Reading the mock store without a write should simulate the file not found case because
463         // |readRawData| would return null.
464         mWifiConfigStore.registerStoreData(sharedStoreData);
465         mWifiConfigStore.registerStoreData(userStoreData);
466         // Read both share and user config store.
467         mWifiConfigStore.setUserStores(mUserStores);
468         mWifiConfigStore.read();
469 
470         // Ensure that we got the call to deserialize empty shared & user data.
471         verify(userStoreData).resetData();
472         verify(userStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
473         verify(sharedStoreData).resetData();
474         verify(sharedStoreData).deserializeData(eq(null), anyInt(), anyInt(), any());
475     }
476 
477     /**
478      * Tests the read API behaviour after a write to the shared store file when the user
479      * store file is null.
480      * Expected behaviour: The read should return the same data that was last written.
481      */
482     @Test
testReadAfterWriteWithNoUserStore()483     public void testReadAfterWriteWithNoUserStore() throws Exception {
484         // Setup data container.
485         mWifiConfigStore.registerStoreData(mSharedStoreData);
486         mSharedStoreData.setData(TEST_SHARE_DATA);
487 
488         // Perform write for the share store file.
489         mWifiConfigStore.write(true);
490         mWifiConfigStore.read();
491         // Verify data content for both user and share data.
492         assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
493     }
494 
495     /**
496      * Verifies that a read operation will reset the data in the data container, to avoid
497      * any stale data from previous read.
498      *
499      * @throws Exception
500      */
501     @Test
testReadWillResetStoreData()502     public void testReadWillResetStoreData() throws Exception {
503         // Register and setup store data.
504         mWifiConfigStore.registerStoreData(mSharedStoreData);
505         mWifiConfigStore.registerStoreData(mUserStoreData);
506 
507         // Perform force write with empty data content to both user and share store file.
508         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
509         mWifiConfigStore.write(true);
510 
511         // Setup data container with some value.
512         mUserStoreData.setData(TEST_USER_DATA);
513         mSharedStoreData.setData(TEST_SHARE_DATA);
514 
515         // Perform read of both user and share store file and verify data in the data container
516         // is in sync (empty) with what is in the file.
517         mWifiConfigStore.read();
518         assertNull(mSharedStoreData.getData());
519         assertNull(mUserStoreData.getData());
520     }
521 
522     /**
523      * Verify that a store file contained WiFi configuration store data (network list and
524      * deleted ephemeral SSID list) using the predefined test XML data is read and parsed
525      * correctly.
526      *
527      * @throws Exception
528      */
529     @Test
testReadWifiConfigStoreData()530     public void testReadWifiConfigStoreData() throws Exception {
531         // Setup network list.
532         NetworkListStoreData networkList = new NetworkListUserStoreData(mContext);
533         mWifiConfigStore.registerStoreData(networkList);
534         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
535         openNetwork.creatorName = TEST_CREATOR_NAME;
536         openNetwork.setIpConfiguration(
537                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
538         openNetwork.setRandomizedMacAddress(TEST_RANDOMIZED_MAC);
539         List<WifiConfiguration> userConfigs = new ArrayList<>();
540         userConfigs.add(openNetwork);
541 
542         // Setup user store XML bytes.
543         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
544                 openNetwork.getKey().replaceAll("\"", "&quot;"),
545                 openNetwork.SSID.replaceAll("\"", "&quot;"),
546                 openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
547                 openNetwork.getRandomizedMacAddress());
548         byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
549         mUserStore.storeRawDataToWrite(xmlBytes);
550 
551         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
552         WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
553                 userConfigs, networkList.getConfigurations());
554     }
555 
556     /**
557      * Verify that the WiFi configuration store data containing network list and deleted
558      * ephemeral SSID list are serialized correctly, matches the predefined test XML data.
559      *
560      * @throws Exception
561      */
562     @Test
testWriteWifiConfigStoreData()563     public void testWriteWifiConfigStoreData() throws Exception {
564         // Setup user store.
565         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
566 
567         // Setup network list store data.
568         NetworkListStoreData networkList = new NetworkListUserStoreData(mContext);
569         mWifiConfigStore.registerStoreData(networkList);
570         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
571         openNetwork.creatorName = TEST_CREATOR_NAME;
572         openNetwork.setIpConfiguration(
573                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
574         openNetwork.setRandomizedMacAddress(TEST_RANDOMIZED_MAC);
575         List<WifiConfiguration> userConfigs = new ArrayList<>();
576         userConfigs.add(openNetwork);
577         networkList.setConfigurations(userConfigs);
578 
579         // Setup expected XML bytes.
580         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
581                 openNetwork.getKey().replaceAll("\"", "&quot;"),
582                 openNetwork.SSID.replaceAll("\"", "&quot;"),
583                 openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
584                 openNetwork.getRandomizedMacAddress());
585 
586         mWifiConfigStore.write(true);
587         // Verify the user store content.
588         assertEquals(xmlString, new String(mUserStore.getStoreBytes()));
589     }
590 
591     /**
592      * Verify that a store file contained WiFi configuration store data (network list and
593      * deleted ephemeral SSID list) using the predefined test XML data is read and parsed
594      * correctly.
595      *
596      * @throws Exception
597      */
598     @Test
testReadWifiConfigStoreDataIndicateClientsThatThereIsNoDataForThem()599     public void testReadWifiConfigStoreDataIndicateClientsThatThereIsNoDataForThem()
600             throws Exception {
601         // Set both the user store & shared store files.
602         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
603 
604         String storeData1Name = "test1";
605         String storeData2Name = "test2";
606         StoreData storeData1 = mock(StoreData.class);
607         StoreData storeData2 = mock(StoreData.class);
608 
609         assertTrue(mWifiConfigStore.registerStoreData(storeData1));
610         assertTrue(mWifiConfigStore.registerStoreData(storeData2));
611 
612         String fileContentsXmlStringWithOnlyStoreData1 =
613                 String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE, storeData1Name);
614         String fileContentsXmlStringWithOnlyStoreData2 =
615                 String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE, storeData2Name);
616         String fileContentsXmlStringWithStoreData1AndStoreData2 =
617                 String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_TWO_DATA_SOURCE,
618                         storeData1Name, storeData2Name);
619 
620         // Scenario 1: StoreData1 in shared store file.
621         when(storeData1.getName()).thenReturn(storeData1Name);
622         when(storeData2.getName()).thenReturn(storeData2Name);
623         when(storeData1.getStoreFileId())
624                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
625         when(storeData2.getStoreFileId())
626                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
627         mSharedStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData1.getBytes());
628         mUserStore.storeRawDataToWrite(null);
629 
630         mWifiConfigStore.read();
631         verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
632         verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
633         verify(storeData2).deserializeData(eq(null), anyInt(), anyInt(), any());
634         reset(storeData1, storeData2);
635 
636         // Scenario 2: StoreData2 in user store file.
637         when(storeData1.getName()).thenReturn(storeData1Name);
638         when(storeData2.getName()).thenReturn(storeData2Name);
639         when(storeData1.getStoreFileId())
640                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
641         when(storeData2.getStoreFileId())
642                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
643         mSharedStore.storeRawDataToWrite(null);
644         mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
645 
646         mWifiConfigStore.read();
647         verify(storeData1).deserializeData(eq(null), anyInt(), anyInt(), any());
648         verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
649         verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
650         reset(storeData1, storeData2);
651 
652         // Scenario 3: StoreData1 in shared store file & StoreData2 in user store file.
653         when(storeData1.getName()).thenReturn(storeData1Name);
654         when(storeData2.getName()).thenReturn(storeData2Name);
655         when(storeData1.getStoreFileId())
656                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
657         when(storeData2.getStoreFileId())
658                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
659         mSharedStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData1.getBytes());
660         mUserStore.storeRawDataToWrite(fileContentsXmlStringWithOnlyStoreData2.getBytes());
661 
662         mWifiConfigStore.read();
663         verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
664         verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
665         verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
666         verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
667         reset(storeData1, storeData2);
668 
669         // Scenario 4: StoreData1 & StoreData2 in shared store file.
670         when(storeData1.getName()).thenReturn(storeData1Name);
671         when(storeData2.getName()).thenReturn(storeData2Name);
672         when(storeData1.getStoreFileId())
673                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
674         when(storeData2.getStoreFileId())
675                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
676         mSharedStore.storeRawDataToWrite(
677                 fileContentsXmlStringWithStoreData1AndStoreData2.getBytes());
678         mUserStore.storeRawDataToWrite(null);
679 
680         mWifiConfigStore.read();
681         verify(storeData1).deserializeData(notNull(), anyInt(), anyInt(), any());
682         verify(storeData1, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
683         verify(storeData2).deserializeData(notNull(), anyInt(), anyInt(), any());
684         verify(storeData2, never()).deserializeData(eq(null), anyInt(), anyInt(), any());
685         reset(storeData1, storeData2);
686     }
687 
688     /**
689      * Tests the write API behavior when all the store data's registered for a given store file
690      * has no new data to write.
691      * Expected behaviour: The write should not trigger a new file write for that specific store
692      * file.
693      */
694     @Test
testWriteWithNoNewData()695     public void testWriteWithNoNewData() throws Exception {
696         StoreData sharedStoreData = mock(StoreData.class);
697         when(sharedStoreData.getStoreFileId())
698                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
699         when(sharedStoreData.hasNewDataToSerialize()).thenReturn(true);
700         when(sharedStoreData.getName()).thenReturn("sharedStoreData");
701 
702         StoreData userStoreData = mock(StoreData.class);
703         when(userStoreData.getStoreFileId())
704                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
705         when(userStoreData.hasNewDataToSerialize()).thenReturn(true);
706         when(userStoreData.getName()).thenReturn("userStoreData");
707 
708         StoreData userStoreNetworkSuggestionsData =
709                 mock(StoreData.class);
710         when(userStoreNetworkSuggestionsData.getStoreFileId())
711                 .thenReturn(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS);
712         when(userStoreNetworkSuggestionsData.hasNewDataToSerialize()).thenReturn(false);
713         when(userStoreNetworkSuggestionsData.getName())
714                 .thenReturn("userStoreNetworkSuggestionsData");
715 
716         assertTrue(mWifiConfigStore.registerStoreData(sharedStoreData));
717         assertTrue(mWifiConfigStore.registerStoreData(userStoreData));
718         assertTrue(mWifiConfigStore.registerStoreData(userStoreNetworkSuggestionsData));
719 
720         // Write both share and user config store.
721         mWifiConfigStore.setUserStores(mUserStores);
722 
723         // Now trigger a write.
724         mWifiConfigStore.write(true);
725 
726         verify(sharedStoreData).hasNewDataToSerialize();
727         verify(userStoreData).hasNewDataToSerialize();
728         verify(userStoreNetworkSuggestionsData).hasNewDataToSerialize();
729 
730         // Verify that we serialized data from the first 2 data source, but not from the last one.
731         verify(sharedStoreData).serializeData(any(), any());
732         verify(userStoreData).serializeData(any(), any());
733         verify(userStoreNetworkSuggestionsData, never()).serializeData(any(), any());
734     }
735 
736     /**
737      * Verify that we gracefully skip unknown section when reading an user store file.
738      */
739     @Test
testReadUserStoreContainedUnknownSection()740     public void testReadUserStoreContainedUnknownSection() throws Exception {
741         String storeFileData =
742                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
743                         + "<WifiConfigStoreData>\n"
744                         + "<int name=\"Version\" value=\"1\" />\n"
745                         + "<UnknownTag>\n"    // No StoreData registered to handle this tag.
746                         + "</UnknownTag>\n"
747                         + "</WifiConfigStoreData>\n";
748         mUserStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
749         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
750     }
751 
752     /**
753      * Verify that we gracefully skip unknown section when reading a shared store file.
754      */
755     @Test
testReadShareStoreContainedUnknownSection()756     public void testReadShareStoreContainedUnknownSection() throws Exception {
757         String storeFileData =
758                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
759                         + "<WifiConfigStoreData>\n"
760                         + "<int name=\"Version\" value=\"1\" />\n"
761                         + "<UnknownTag>\n"    // No StoreData registered to handle this tag.
762                         + "</UnknownTag>\n"
763                         + "</WifiConfigStoreData>\n";
764         mSharedStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
765         mWifiConfigStore.read();
766     }
767 
768     /**
769      * Tests the read API behaviour when the config store file is version 1.
770      * Expected behaviour: The read should be successful and send the data to the corresponding
771      *                     {@link StoreData} instance.
772      */
773     @Test
testReadVersion1StoreFile()774     public void testReadVersion1StoreFile() throws Exception {
775         // Register data container.
776         StoreData sharedStoreData = mock(StoreData.class);
777         when(sharedStoreData.getStoreFileId())
778                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
779         when(sharedStoreData.getName()).thenReturn(TEST_SHARE_DATA);
780         StoreData userStoreData = mock(StoreData.class);
781         when(userStoreData.getStoreFileId())
782                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
783         when(userStoreData.getName()).thenReturn(TEST_USER_DATA);
784         mWifiConfigStore.registerStoreData(sharedStoreData);
785         mWifiConfigStore.registerStoreData(userStoreData);
786 
787         // Read both share and user config store.
788         mWifiConfigStore.setUserStores(mUserStores);
789 
790         // Now store some content in the shared and user data files.
791         mUserStore.storeRawDataToWrite(
792                 String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE,
793                         TEST_USER_DATA).getBytes());
794         mSharedStore.storeRawDataToWrite(
795                 String.format(TEST_DATA_XML_STRING_FORMAT_V1_WITH_ONE_DATA_SOURCE,
796                         TEST_SHARE_DATA).getBytes());
797 
798         // Read and verify the data content in the store file (metadata stripped out) has been sent
799         // to the corresponding store data when integrity check passes.
800         mWifiConfigStore.read();
801         verify(sharedStoreData, times(1)).deserializeData(
802                 any(XmlPullParser.class), anyInt(),
803                 eq(WifiConfigStore.INITIAL_CONFIG_STORE_DATA_VERSION), any());
804         verify(userStoreData, times(1)).deserializeData(
805                 any(XmlPullParser.class), anyInt(),
806                 eq(WifiConfigStore.INITIAL_CONFIG_STORE_DATA_VERSION), any());
807     }
808 
809     /**
810      * Tests the read API behaviour to ensure that the integrity data is parsed from the file.
811      */
812     @Test
testReadVersion2StoreFile()813     public void testReadVersion2StoreFile() throws Exception {
814         byte[] encryptedData = new byte[0];
815         byte[] iv = new byte[0];
816         Random random = new Random();
817         random.nextBytes(encryptedData);
818         random.nextBytes(iv);
819 
820         // Register data container.
821         StoreData sharedStoreData = mock(StoreData.class);
822         when(sharedStoreData.getStoreFileId())
823                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
824         when(sharedStoreData.getName()).thenReturn(TEST_SHARE_DATA);
825         when(sharedStoreData.hasNewDataToSerialize()).thenReturn(true);
826         StoreData userStoreData = mock(StoreData.class);
827         when(userStoreData.getStoreFileId())
828                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
829         when(userStoreData.getName()).thenReturn(TEST_USER_DATA);
830         when(userStoreData.hasNewDataToSerialize()).thenReturn(true);
831         mWifiConfigStore.registerStoreData(sharedStoreData);
832         mWifiConfigStore.registerStoreData(userStoreData);
833 
834         // Read both share and user config store.
835         mWifiConfigStore.setUserStores(mUserStores);
836 
837         // Now store some content in the shared and user data files with encrypted data from above.
838         mUserStore.storeRawDataToWrite(
839                 String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
840                         HexEncoding.encodeToString(encryptedData),
841                         HexEncoding.encodeToString(iv),
842                         TEST_USER_DATA).getBytes());
843         mSharedStore.storeRawDataToWrite(
844                 String.format(TEST_DATA_XML_STRING_FORMAT_V2_WITH_ONE_DATA_SOURCE,
845                         HexEncoding.encodeToString(encryptedData),
846                         HexEncoding.encodeToString(iv),
847                         TEST_SHARE_DATA).getBytes());
848 
849         // Read and verify the data content in the store file (metadata stripped out) has been sent
850         // to the corresponding store data.
851         mWifiConfigStore.read();
852         verify(sharedStoreData, times(1))
853                 .deserializeData(any(XmlPullParser.class), anyInt(),
854                         eq(WifiConfigStore.INTEGRITY_CONFIG_STORE_DATA_VERSION), any());
855         verify(userStoreData, times(1))
856                 .deserializeData(any(XmlPullParser.class), anyInt(),
857                         eq(WifiConfigStore.INTEGRITY_CONFIG_STORE_DATA_VERSION), any());
858     }
859 
860     /**
861      * Tests the complete migration path all the way from reading from the migration stream to
862      * parsing the XML data and sending it to the appropriate registered data sources.
863      */
864     @Test
testMigration()865     public void testMigration() throws Exception {
866         // Setup both shared & user store migrations.
867         StoreFile sharedStoreFile1 = mock(StoreFile.class);
868         when(sharedStoreFile1.getFileId())
869                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
870         StoreFile sharedStoreFile2 = mock(StoreFile.class);
871         when(sharedStoreFile2.getFileId())
872                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_SOFTAP);
873         StoreFile userStoreFile1 = mock(StoreFile.class);
874         when(userStoreFile1.getFileId())
875                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
876         StoreFile userStoreFile2 = mock(StoreFile.class);
877         when(userStoreFile2.getFileId())
878                 .thenReturn(WifiConfigStore.STORE_FILE_USER_NETWORK_SUGGESTIONS);
879         mWifiConfigStore = new WifiConfigStore(mContext, new Handler(mLooper.getLooper()), mClock,
880                 mWifiMetrics, Arrays.asList(sharedStoreFile1, sharedStoreFile2));
881         mWifiConfigStore.setUserStores(Arrays.asList(userStoreFile1, userStoreFile2));
882 
883         // Register data container.
884         StoreData sharedStoreData = mock(StoreData.class);
885         when(sharedStoreData.getStoreFileId())
886                 .thenReturn(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
887         when(sharedStoreData.getName()).thenReturn(TEST_SHARE_DATA);
888         StoreData userStoreData = mock(StoreData.class);
889         when(userStoreData.getStoreFileId())
890                 .thenReturn(WifiConfigStore.STORE_FILE_USER_GENERAL);
891         when(userStoreData.getName()).thenReturn(TEST_USER_DATA);
892         mWifiConfigStore.registerStoreData(sharedStoreData);
893         mWifiConfigStore.registerStoreData(userStoreData);
894 
895         // Migration data
896         InputStream sharedStream1 = mock(InputStream.class);
897         InputStream sharedStream2 = mock(InputStream.class);
898         InputStream userStream1 = mock(InputStream.class);
899         InputStream userStream2 = mock(InputStream.class);
900         when(WifiMigration.convertAndRetrieveSharedConfigStoreFile(
901                 WifiMigration.STORE_FILE_SHARED_GENERAL))
902                 .thenReturn(sharedStream1);
903         when(WifiMigration.convertAndRetrieveSharedConfigStoreFile(
904                 WifiMigration.STORE_FILE_SHARED_SOFTAP))
905                 .thenReturn(sharedStream2);
906         when(WifiMigration.convertAndRetrieveUserConfigStoreFile(
907                 eq(WifiMigration.STORE_FILE_USER_GENERAL), any()))
908                 .thenReturn(userStream1);
909         when(WifiMigration.convertAndRetrieveUserConfigStoreFile(
910                 eq(WifiMigration.STORE_FILE_USER_NETWORK_SUGGESTIONS), any()))
911                 .thenReturn(userStream2);
912 
913         byte[] sharedStoreXmlBytes =
914                 String.format(TEST_DATA_XML_STRING_FORMAT_V3_WITH_ONE_DATA_SOURCE,
915                         TEST_SHARE_DATA).getBytes();
916         byte[] userStoreXmlBytes =
917                 String.format(TEST_DATA_XML_STRING_FORMAT_V3_WITH_ONE_DATA_SOURCE,
918                         TEST_USER_DATA).getBytes();
919         when(sharedStream1.available())
920                 .thenReturn(sharedStoreXmlBytes.length) // first time return file contents, then 0.
921                 .thenReturn(0);
922         when(sharedStream2.available())
923                 .thenReturn(sharedStoreXmlBytes.length) // first time return file contents, then 0.
924                 .thenReturn(0);
925         when(userStream1.available())
926                 .thenReturn(userStoreXmlBytes.length) // first time return file contents, then 0.
927                 .thenReturn(0);
928         when(userStream2.available())
929                 .thenReturn(userStoreXmlBytes.length) // first time return file contents, then 0.
930                 .thenReturn(0);
931         Answer sharedStreamReadAnswer = new MockAnswerUtil.AnswerWithArguments() {
932             public int answer(byte[] b, int off, int len) {
933                 System.arraycopy(sharedStoreXmlBytes, 0, b, 0, sharedStoreXmlBytes.length);
934                 return sharedStoreXmlBytes.length;
935             }
936         };
937         Answer userStreamReadAnswer = new MockAnswerUtil.AnswerWithArguments() {
938             public int answer(byte[] b, int off, int len) {
939                 System.arraycopy(userStoreXmlBytes, 0, b, 0, userStoreXmlBytes.length);
940                 return userStoreXmlBytes.length;
941             }
942         };
943         when(sharedStream1.read(any(byte[].class), anyInt(), anyInt()))
944                 .thenAnswer(sharedStreamReadAnswer) // first time return file contents, then 0.
945                 .thenReturn(0);
946         when(sharedStream2.read(any(byte[].class), anyInt(), anyInt()))
947                 .thenAnswer(sharedStreamReadAnswer) // first time return file contents, then 0.
948                 .thenReturn(0);
949         when(userStream1.read(any(byte[].class), anyInt(), anyInt()))
950                 .thenAnswer(userStreamReadAnswer) // first time return file contents, then 0.
951                 .thenReturn(0);
952         when(userStream2.read(any(byte[].class), anyInt(), anyInt()))
953                 .thenAnswer(userStreamReadAnswer) // first time return file contents, then 0.
954                 .thenReturn(0);
955 
956         // Trigger read.
957         mWifiConfigStore.read();
958 
959         // Verify that we read the data out of all the migration streams & we didn't read
960         // from the files on disk.
961         verify(sharedStream1, times(2)).available();
962         verify(sharedStream1, times(2)).read(any(), anyInt(), anyInt());
963         verify(sharedStream2, times(2)).available();
964         verify(sharedStream2, times(2)).read(any(), anyInt(), anyInt());
965         verify(userStream1, times(2)).available();
966         verify(userStream1, times(2)).read(any(), anyInt(), anyInt());
967         verify(userStream2, times(2)).available();
968         verify(userStream2, times(2)).read(any(), anyInt(), anyInt());
969 
970         // Verify that we correctly deserialized the data and sent it to the corresponding sources.
971         verify(sharedStoreData, times(1))
972                 .deserializeData(any(XmlPullParser.class), anyInt(),
973                         eq(WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION), any());
974         verify(userStoreData, times(1))
975                 .deserializeData(any(XmlPullParser.class), anyInt(),
976                         eq(WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION), any());
977 
978         // Verify we did not read from the real store files.
979         verify(sharedStoreFile1, never()).readRawData();
980         verify(sharedStoreFile2, never()).readRawData();
981         verify(userStoreFile1, never()).readRawData();
982         verify(userStoreFile2, never()).readRawData();
983     }
984 
985     /**
986      * Mock Store File to redirect all file writes from WifiConfigStore to local buffers.
987      * This can be used to examine the data output by WifiConfigStore.
988      */
989     private class MockStoreFile extends StoreFile {
990         private byte[] mStoreBytes;
991         private boolean mStoreWritten;
992 
MockStoreFile(@ifiConfigStore.StoreFileId int fileId)993         MockStoreFile(@WifiConfigStore.StoreFileId int fileId) {
994             super(new File("MockStoreFile"), fileId, UserHandle.ALL, mEncryptionUtil);
995         }
996 
997         @Override
readRawData()998         public byte[] readRawData() {
999             return mStoreBytes;
1000         }
1001 
1002         @Override
storeRawDataToWrite(byte[] data)1003         public void storeRawDataToWrite(byte[] data) {
1004             mStoreBytes = data;
1005             mStoreWritten = false;
1006         }
1007 
1008         @Override
writeBufferedRawData()1009         public void writeBufferedRawData() {
1010             if (!ArrayUtils.isEmpty(mStoreBytes)) {
1011                 mStoreWritten = true;
1012             }
1013         }
1014 
getStoreBytes()1015         public byte[] getStoreBytes() {
1016             return mStoreBytes;
1017         }
1018 
isStoreWritten()1019         public boolean isStoreWritten() {
1020             return mStoreWritten;
1021         }
1022     }
1023 
1024     /**
1025      * Mock data container for providing test data for the store file.
1026      */
1027     private class MockStoreData implements StoreData {
1028         private static final String XML_TAG_TEST_HEADER = "TestHeader";
1029         private static final String XML_TAG_TEST_DATA = "TestData";
1030 
1031         private @WifiConfigStore.StoreFileId int mFileId;
1032         private String mData;
1033         private boolean mHasAnyNewData = true;
1034 
MockStoreData(@ifiConfigStore.StoreFileId int fileId)1035         MockStoreData(@WifiConfigStore.StoreFileId int fileId) {
1036             mFileId = fileId;
1037         }
1038 
1039         @Override
serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil)1040         public void serializeData(XmlSerializer out, WifiConfigStoreEncryptionUtil encryptionUtil)
1041                 throws XmlPullParserException, IOException {
1042             XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mData);
1043         }
1044 
1045         @Override
deserializeData(XmlPullParser in, int outerTagDepth, int version, WifiConfigStoreEncryptionUtil encryptionUtil)1046         public void deserializeData(XmlPullParser in, int outerTagDepth, int version,
1047                 WifiConfigStoreEncryptionUtil encryptionUtil)
1048                 throws XmlPullParserException, IOException {
1049             if (in == null) {
1050                 return;
1051             }
1052             mData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA);
1053         }
1054 
1055         @Override
resetData()1056         public void resetData() {
1057             mData = null;
1058         }
1059 
1060         @Override
hasNewDataToSerialize()1061         public boolean hasNewDataToSerialize() {
1062             return mHasAnyNewData;
1063         }
1064 
1065         @Override
getName()1066         public String getName() {
1067             return XML_TAG_TEST_HEADER;
1068         }
1069 
1070         @Override
getStoreFileId()1071         public @WifiConfigStore.StoreFileId int getStoreFileId() {
1072             return mFileId;
1073         }
1074 
getData()1075         public String getData() {
1076             return mData;
1077         }
1078 
setData(String data)1079         public void setData(String data) {
1080             mData = data;
1081         }
1082 
setHasAnyNewData(boolean hasAnyNewData)1083         public void setHasAnyNewData(boolean hasAnyNewData) {
1084             mHasAnyNewData = hasAnyNewData;
1085         }
1086     }
1087 }
1088