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