1 /*
2  * Copyright (C) 2015 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 package com.android.providers.settings;
17 
18 import static junit.framework.Assert.assertEquals;
19 import static junit.framework.Assert.assertFalse;
20 import static junit.framework.Assert.assertNull;
21 import static junit.framework.Assert.assertTrue;
22 import static junit.framework.Assert.fail;
23 
24 import android.aconfig.Aconfig;
25 import android.aconfig.Aconfig.parsed_flag;
26 import android.aconfig.Aconfig.parsed_flags;
27 import android.aconfigd.AconfigdFlagInfo;
28 import android.os.Looper;
29 import android.platform.test.annotations.RequiresFlagsEnabled;
30 import android.platform.test.flag.junit.CheckFlagsRule;
31 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
32 import android.util.Xml;
33 import android.util.proto.ProtoOutputStream;
34 
35 import androidx.test.InstrumentationRegistry;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import com.android.modules.utils.TypedXmlSerializer;
39 
40 import android.platform.test.annotations.EnableFlags;
41 import android.platform.test.annotations.DisableFlags;
42 import android.platform.test.flag.junit.SetFlagsRule;
43 
44 import com.google.common.base.Strings;
45 
46 import java.io.ByteArrayOutputStream;
47 import java.io.File;
48 import java.io.FileOutputStream;
49 import java.io.PrintStream;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Map;
53 
54 import org.junit.After;
55 import org.junit.Before;
56 import org.junit.Rule;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 
60 @RunWith(AndroidJUnit4.class)
61 public class SettingsStateTest {
62     @Rule
63     public final CheckFlagsRule mCheckFlagsRule =
64             DeviceFlagsValueProvider.createCheckFlagsRule();
65 
66     public static final String CRAZY_STRING =
67             "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000b\u000c\r" +
68                     "\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a" +
69                     "\u001b\u001c\u001d\u001e\u001f\u0020" +
70                     "fake_setting_value_1" +
71                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
72                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
73                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
74                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
75                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
76                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
77                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
78                     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
79                     "\u1000 \u2000 \u5000 \u8000 \uc000 \ue000" +
80                     "\ud800\udc00\udbff\udfff" + // surrogate pairs
81                     "\uD800ab\uDC00 " + // broken surrogate pairs
82                     "日本語";
83 
84     private static final String TEST_PACKAGE = "package";
85     private static final String SYSTEM_PACKAGE = "android";
86     private static final String SETTING_NAME = "test_setting";
87 
88     private static final String FLAG_NAME_1 = "namespace123/flag456";
89     private static final String FLAG_NAME_1_STAGED = "staged/namespace123*flag456";
90     private static final String FLAG_NAME_2 = "not_staged/flag101";
91 
92     private static final String INVALID_STAGED_FLAG_1 = "stagednamespace*flagName";
93     private static final String INVALID_STAGED_FLAG_2 = "staged/";
94     private static final String INVALID_STAGED_FLAG_3 = "staged/namespace*";
95     private static final String INVALID_STAGED_FLAG_4 = "staged/*flagName";
96 
97     private static final String VALID_STAGED_FLAG_1 = "staged/namespace*flagName";
98     private static final String VALID_STAGED_FLAG_1_TRANSFORMED = "namespace/flagName";
99 
100     private static final String VALUE1 = "5";
101     private static final String VALUE2 = "6";
102 
103     private final Object mLock = new Object();
104 
105     private File mSettingsFile;
106 
107     @Before
setUp()108     public void setUp() {
109         mSettingsFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "setting.xml");
110         mSettingsFile.delete();
111     }
112 
113     @After
tearDown()114     public void tearDown() throws Exception {
115         if (mSettingsFile != null) {
116             mSettingsFile.delete();
117         }
118     }
119 
120     @Test
testLoadValidAconfigProto()121     public void testLoadValidAconfigProto() {
122         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
123         Object lock = new Object();
124         SettingsState settingsState = new SettingsState(
125                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
126                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
127         parsed_flags flags = parsed_flags
128                 .newBuilder()
129                 .addParsedFlag(parsed_flag
130                     .newBuilder()
131                         .setPackage("com.android.flags")
132                         .setName("flag1")
133                         .setNamespace("test_namespace")
134                         .setDescription("test flag")
135                         .addBug("12345678")
136                         .setState(Aconfig.flag_state.DISABLED)
137                         .setPermission(Aconfig.flag_permission.READ_WRITE))
138                 .addParsedFlag(parsed_flag
139                     .newBuilder()
140                         .setPackage("com.android.flags")
141                         .setName("flag2")
142                         .setNamespace("test_namespace")
143                         .setDescription("another test flag")
144                         .addBug("12345678")
145                         .setState(Aconfig.flag_state.ENABLED)
146                         .setPermission(Aconfig.flag_permission.READ_ONLY))
147                 .build();
148 
149         AconfigdFlagInfo flag1 = AconfigdFlagInfo.newBuilder()
150                                                 .setPackageName("com.android.flags")
151                                                 .setFlagName("flag1")
152                                                 .setDefaultFlagValue("false")
153                                                 .setIsReadWrite(true)
154                                                 .build();
155         AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder()
156                                                 .setPackageName("com.android.flags")
157                                                 .setFlagName("flag2")
158                                                 .setDefaultFlagValue("true")
159                                                 .setIsReadWrite(false)
160                                                 .build();
161         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
162 
163         synchronized (lock) {
164             Map<String, Map<String, String>> defaults = new HashMap<>();
165             settingsState.loadAconfigDefaultValues(
166                 flags.toByteArray(), defaults, flagInfoDefault);
167             Map<String, String> namespaceDefaults = defaults.get("test_namespace");
168             assertEquals(2, namespaceDefaults.keySet().size());
169 
170             assertEquals("false", namespaceDefaults.get("test_namespace/com.android.flags.flag1"));
171             assertEquals("true", namespaceDefaults.get("test_namespace/com.android.flags.flag2"));
172         }
173 
174         assertEquals(flag1, flagInfoDefault.get(flag1.getFullFlagName()));
175         assertEquals(flag2, flagInfoDefault.get(flag2.getFullFlagName()));
176     }
177 
178     @Test
testSkipLoadingAconfigFlagWithMissingFields()179     public void testSkipLoadingAconfigFlagWithMissingFields() {
180         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
181         Object lock = new Object();
182         SettingsState settingsState = new SettingsState(
183                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
184                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
185 
186         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
187 
188         parsed_flags flags = parsed_flags
189                 .newBuilder()
190                 .addParsedFlag(parsed_flag
191                     .newBuilder()
192                         .setDescription("test flag")
193                         .addBug("12345678")
194                         .setState(Aconfig.flag_state.DISABLED)
195                         .setPermission(Aconfig.flag_permission.READ_WRITE))
196                 .build();
197 
198         synchronized (lock) {
199             Map<String, Map<String, String>> defaults = new HashMap<>();
200             settingsState.loadAconfigDefaultValues(
201                 flags.toByteArray(), defaults, flagInfoDefault);
202 
203             Map<String, String> namespaceDefaults = defaults.get("test_namespace");
204             assertEquals(null, namespaceDefaults);
205         }
206     }
207 
208     @Test
209     @RequiresFlagsEnabled(Flags.FLAG_STAGE_ALL_ACONFIG_FLAGS)
testWritingAconfigFlagStages()210     public void testWritingAconfigFlagStages() {
211         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
212         Object lock = new Object();
213         SettingsState settingsState = new SettingsState(
214                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
215                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
216         parsed_flags flags = parsed_flags
217                 .newBuilder()
218                 .addParsedFlag(parsed_flag
219                     .newBuilder()
220                         .setPackage("com.android.flags")
221                         .setName("flag5")
222                         .setNamespace("test_namespace")
223                         .setDescription("test flag")
224                         .addBug("12345678")
225                         .setState(Aconfig.flag_state.DISABLED)
226                         .setPermission(Aconfig.flag_permission.READ_WRITE))
227                 .build();
228         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
229 
230         synchronized (lock) {
231             Map<String, Map<String, String>> defaults = new HashMap<>();
232             settingsState.loadAconfigDefaultValues(
233                 flags.toByteArray(), defaults, flagInfoDefault);
234             settingsState.addAconfigDefaultValuesFromMap(defaults);
235 
236             settingsState.insertSettingLocked("test_namespace/com.android.flags.flag5",
237                     "true", null, false, "com.android.flags");
238             settingsState.insertSettingLocked("test_namespace/com.android.flags.flag6",
239                     "true", null, false, "com.android.flags");
240 
241             assertEquals("true",
242                     settingsState
243                         .getSettingLocked("staged/test_namespace*com.android.flags.flag5")
244                         .getValue());
245             assertEquals(null,
246                     settingsState
247                         .getSettingLocked("test_namespace/com.android.flags.flag5")
248                         .getValue());
249 
250             assertEquals(null,
251                     settingsState
252                         .getSettingLocked("staged/test_namespace*com.android.flags.flag6")
253                         .getValue());
254             assertEquals("true",
255                     settingsState
256                         .getSettingLocked("test_namespace/com.android.flags.flag6")
257                         .getValue());
258         }
259     }
260 
261     @Test
testInvalidAconfigProtoDoesNotCrash()262     public void testInvalidAconfigProtoDoesNotCrash() {
263         Map<String, Map<String, String>> defaults = new HashMap<>();
264         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
265         SettingsState settingsState = getSettingStateObject();
266         settingsState.loadAconfigDefaultValues(
267             "invalid protobuf".getBytes(), defaults, flagInfoDefault);
268     }
269 
270     @Test
testIsBinary()271     public void testIsBinary() {
272         assertFalse(SettingsState.isBinary(" abc 日本語"));
273 
274         for (char ch = 0x20; ch < 0xd800; ch++) {
275             assertFalse("ch=" + Integer.toString(ch, 16),
276                     SettingsState.isBinary(String.valueOf(ch)));
277         }
278         for (char ch = 0xe000; ch < 0xfffe; ch++) {
279             assertFalse("ch=" + Integer.toString(ch, 16),
280                     SettingsState.isBinary(String.valueOf(ch)));
281         }
282 
283         for (char ch = 0x0000; ch < 0x20; ch++) {
284             assertTrue("ch=" + Integer.toString(ch, 16),
285                     SettingsState.isBinary(String.valueOf(ch)));
286         }
287         for (char ch = 0xd800; ch < 0xe000; ch++) {
288             assertTrue("ch=" + Integer.toString(ch, 16),
289                     SettingsState.isBinary(String.valueOf(ch)));
290         }
291         assertTrue(SettingsState.isBinary("\ufffe"));
292         assertTrue(SettingsState.isBinary("\uffff"));
293         try {
294             assertFalse(SettingsState.isBinary(null));
295             fail("NullPointerException expected");
296         } catch (NullPointerException expected) {
297         }
298     }
299 
300     /** Make sure we won't pass invalid characters to XML serializer. */
301     @Test
testWriteReadNoCrash()302     public void testWriteReadNoCrash() throws Exception {
303         ByteArrayOutputStream os = new ByteArrayOutputStream();
304 
305         TypedXmlSerializer serializer = Xml.resolveSerializer(os);
306         serializer.startDocument(null, true);
307 
308         for (int ch = 0; ch < 0x10000; ch++) {
309             checkWriteSingleSetting("char=0x" + Integer.toString(ch, 16), serializer,
310                     "key", String.valueOf((char) ch));
311         }
312         checkWriteSingleSetting(serializer, "k", "");
313         checkWriteSingleSetting(serializer, "x", "abc");
314         checkWriteSingleSetting(serializer, "abc", CRAZY_STRING);
315         checkWriteSingleSetting(serializer, "def", null);
316 
317         // Invlid input, but shouoldn't crash.
318         checkWriteSingleSetting(serializer, null, null);
319         checkWriteSingleSetting(serializer, CRAZY_STRING, null);
320         SettingsState.writeSingleSetting(
321                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
322                 serializer, null, "k", "v", null, "package", null, false, false);
323         SettingsState.writeSingleSetting(
324                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
325                 serializer, "1", "k", "v", null, null, null, false, false);
326     }
327 
checkWriteSingleSetting(TypedXmlSerializer serializer, String key, String value)328     private void checkWriteSingleSetting(TypedXmlSerializer serializer, String key, String value)
329             throws Exception {
330         checkWriteSingleSetting(key + "/" + value, serializer, key, value);
331     }
332 
checkWriteSingleSetting(String msg, TypedXmlSerializer serializer, String key, String value)333     private void checkWriteSingleSetting(String msg, TypedXmlSerializer serializer,
334             String key, String value) throws Exception {
335         // Make sure the XML serializer won't crash.
336         SettingsState.writeSingleSetting(
337                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
338                 serializer, "1", key, value, null, "package", null, false, false);
339     }
340 
341     /**
342      * Make sure settings can be written to a file and also can be read.
343      */
344     @Test
testReadWrite()345     public void testReadWrite() {
346         final Object lock = new Object();
347 
348         assertFalse(mSettingsFile.exists());
349         final SettingsState ssWriter =
350                 new SettingsState(
351                         InstrumentationRegistry.getContext(), lock, mSettingsFile, 1,
352                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
353         ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
354 
355         ssWriter.insertSettingLocked("k1", "\u0000", null, false, "package");
356         ssWriter.insertSettingLocked("k2", "abc", null, false, "p2");
357         ssWriter.insertSettingLocked("k3", null, null, false, "p2");
358         ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3");
359         synchronized (lock) {
360             ssWriter.persistSettingsLocked();
361         }
362         ssWriter.waitForHandler();
363         assertTrue(mSettingsFile.exists());
364         final SettingsState ssReader =
365                 new SettingsState(
366                         InstrumentationRegistry.getContext(), lock, mSettingsFile, 1,
367                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
368 
369         synchronized (lock) {
370             assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue());
371             assertEquals("abc", ssReader.getSettingLocked("k2").getValue());
372             assertEquals(null, ssReader.getSettingLocked("k3").getValue());
373             assertEquals(CRAZY_STRING, ssReader.getSettingLocked("k4").getValue());
374         }
375     }
376 
377     /**
378      * In version 120, value "null" meant {code NULL}.
379      */
380     @Test
testUpgrade()381     public void testUpgrade() throws Exception {
382         final Object lock = new Object();
383         final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
384         os.print(
385                 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" +
386                         "<settings version=\"120\">" +
387                         "  <setting id=\"0\" name=\"k0\" value=\"null\" package=\"null\" />" +
388                         "  <setting id=\"1\" name=\"k1\" value=\"\" package=\"\" />" +
389                         "  <setting id=\"2\" name=\"k2\" value=\"v2\" package=\"p2\" />" +
390                         "</settings>");
391         os.close();
392 
393         final SettingsState ss =
394                 new SettingsState(
395                         InstrumentationRegistry.getContext(), lock, mSettingsFile, 1,
396                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
397         synchronized (lock) {
398             SettingsState.Setting s;
399             s = ss.getSettingLocked("k0");
400             assertEquals(null, s.getValue());
401             assertEquals("null", s.getPackageName());
402 
403             s = ss.getSettingLocked("k1");
404             assertEquals("", s.getValue());
405             assertEquals("", s.getPackageName());
406 
407             s = ss.getSettingLocked("k2");
408             assertEquals("v2", s.getValue());
409             assertEquals("p2", s.getPackageName());
410         }
411     }
412 
413     @Test
testInitializeSetting_preserveFlagNotSet()414     public void testInitializeSetting_preserveFlagNotSet() {
415         SettingsState settingsWriter = getSettingStateObject();
416         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
417         settingsWriter.persistSettingsLocked();
418         settingsWriter.waitForHandler();
419 
420         SettingsState settingsReader = getSettingStateObject();
421         assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
422     }
423 
424     @Test
testModifySetting_preserveFlagSet()425     public void testModifySetting_preserveFlagSet() {
426         SettingsState settingsWriter = getSettingStateObject();
427         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
428         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
429         settingsWriter.persistSettingsLocked();
430         settingsWriter.waitForHandler();
431 
432         SettingsState settingsReader = getSettingStateObject();
433         assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
434     }
435 
436     @Test
testModifySettingOverrideableByRestore_preserveFlagNotSet()437     public void testModifySettingOverrideableByRestore_preserveFlagNotSet() {
438         SettingsState settingsWriter = getSettingStateObject();
439         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
440         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
441                 /* overrideableByRestore */ true);
442         settingsWriter.persistSettingsLocked();
443         settingsWriter.waitForHandler();
444 
445         SettingsState settingsReader = getSettingStateObject();
446         assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
447     }
448 
449     @Test
testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged()450     public void testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged() {
451         SettingsState settingsWriter = getSettingStateObject();
452         // Init the setting.
453         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
454         // This modification will set isValuePreservedInRestore = true.
455         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
456         // This modification shouldn't change the value of isValuePreservedInRestore since it's
457         // already been set to true.
458         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
459                 /* overrideableByRestore */ true);
460         settingsWriter.persistSettingsLocked();
461         settingsWriter.waitForHandler();
462 
463         SettingsState settingsReader = getSettingStateObject();
464         assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
465     }
466 
467     @Test
testResetSetting_preservedFlagIsReset()468     public void testResetSetting_preservedFlagIsReset() {
469         SettingsState settingsState = getSettingStateObject();
470         // Initialize the setting.
471         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
472         // Update the setting so that preserved flag is set.
473         settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
474 
475         settingsState.resetSettingLocked(SETTING_NAME);
476         assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
477 
478     }
479 
480     @Test
testModifySettingBySystemPackage_sameValue_preserveFlagNotSet()481     public void testModifySettingBySystemPackage_sameValue_preserveFlagNotSet() {
482         SettingsState settingsState = getSettingStateObject();
483         // Initialize the setting.
484         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
485         // Update the setting.
486         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
487 
488         assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
489     }
490 
491     @Test
testModifySettingBySystemPackage_newValue_preserveFlagSet()492     public void testModifySettingBySystemPackage_newValue_preserveFlagSet() {
493         SettingsState settingsState = getSettingStateObject();
494         // Initialize the setting.
495         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
496         // Update the setting.
497         settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, SYSTEM_PACKAGE);
498 
499         assertTrue(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
500     }
501 
getSettingStateObject()502     private SettingsState getSettingStateObject() {
503         SettingsState settingsState =
504                 new SettingsState(
505                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
506                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
507         settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
508         return settingsState;
509     }
510 
511     @Test
testInsertSetting_memoryUsage()512     public void testInsertSetting_memoryUsage() {
513         SettingsState settingsState = getSettingStateObject();
514         // No exception should be thrown when there is no cap
515         settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
516                 null, false, "p1");
517         settingsState.deleteSettingLocked(SETTING_NAME);
518 
519         settingsState =
520                 new SettingsState(
521                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
522                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
523         // System package doesn't have memory usage limit
524         settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
525                 null, false, SYSTEM_PACKAGE);
526         settingsState.deleteSettingLocked(SETTING_NAME);
527 
528         // Should not throw if usage is under the cap
529         settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975),
530                 null, false, "p1");
531         settingsState.deleteSettingLocked(SETTING_NAME);
532         try {
533             settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
534                     null, false, "p1");
535             fail("Should throw because it exceeded per package memory usage");
536         } catch (IllegalStateException ex) {
537             assertTrue(ex.getMessage().contains("p1"));
538         }
539         try {
540             settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
541                     null, false, "p1");
542             fail("Should throw because it exceeded per package memory usage");
543         } catch (IllegalStateException ex) {
544             assertTrue(ex.getMessage().contains("p1"));
545         }
546         assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
547         try {
548             settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
549                     null, false, "p1");
550             fail("Should throw because it exceeded per package memory usage");
551         } catch (IllegalStateException ex) {
552             assertTrue(ex.getMessage().contains("You are adding too many system settings"));
553         }
554     }
555 
556     @Test
testMemoryUsagePerPackage()557     public void testMemoryUsagePerPackage() {
558         SettingsState settingsState =
559                 new SettingsState(
560                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
561                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
562 
563         // Test inserting one key with default
564         final String testKey1 = SETTING_NAME;
565         final String testValue1 = Strings.repeat("A", 100);
566         settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE);
567         int expectedMemUsage = (testKey1.length() + testValue1.length()
568                 + testValue1.length() /* size for default */) * Character.BYTES;
569         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
570 
571         // Test inserting another key
572         final String testKey2 = SETTING_NAME + "2";
573         settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE);
574         expectedMemUsage += (testKey2.length() + testValue1.length()) * Character.BYTES;
575         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
576 
577         // Test updating first key with new default
578         final String testValue2 = Strings.repeat("A", 300);
579         settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE);
580         expectedMemUsage += (testValue2.length() - testValue1.length()) * 2 * Character.BYTES;
581         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
582 
583         // Test updating first key without new default
584         final String testValue3 = Strings.repeat("A", 50);
585         settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE);
586         expectedMemUsage -= (testValue2.length() - testValue3.length()) * Character.BYTES;
587         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
588 
589         // Test updating second key
590         settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE);
591         expectedMemUsage -= (testValue1.length() - testValue2.length()) * Character.BYTES;
592         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
593 
594         // Test resetting key
595         settingsState.resetSettingLocked(testKey1);
596         expectedMemUsage += (testValue2.length() - testValue3.length()) * Character.BYTES;
597         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
598 
599         // Test resetting default value
600         settingsState.resetSettingDefaultValueLocked(testKey1);
601         expectedMemUsage -= testValue2.length() * Character.BYTES;
602         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
603 
604         // Test deletion
605         settingsState.deleteSettingLocked(testKey2);
606         expectedMemUsage -= (testValue2.length() + testKey2.length() /* key is deleted too */)
607                 * Character.BYTES;
608         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
609 
610         // Test another package with a different key
611         final String testPackage2 = TEST_PACKAGE + "2";
612         final String testKey3 = SETTING_NAME + "3";
613         settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
614         assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
615         final int expectedMemUsage2 = (testKey3.length() + testValue1.length() * 2)
616                 * Character.BYTES;
617         assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
618 
619         // Let system package take over testKey1 which is no longer subject to memory usage counting
620         settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE);
621         assertEquals(0, settingsState.getMemoryUsage(TEST_PACKAGE));
622         assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
623         assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE));
624 
625         // Test invalid value
626         try {
627             settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
628                     TEST_PACKAGE);
629             fail("Should throw because it exceeded per package memory usage");
630         } catch (IllegalStateException ex) {
631             assertTrue(ex.getMessage().contains("You are adding too many system settings"));
632         }
633         assertEquals(0, settingsState.getMemoryUsage(TEST_PACKAGE));
634 
635         // Test invalid key
636         try {
637             settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
638                     TEST_PACKAGE);
639             fail("Should throw because it exceeded per package memory usage");
640         } catch (IllegalStateException ex) {
641             assertTrue(ex.getMessage().contains("You are adding too many system settings"));
642         }
643         assertEquals(0, settingsState.getMemoryUsage(TEST_PACKAGE));
644     }
645 
646     @Test
testLargeSettingKey()647     public void testLargeSettingKey() {
648         SettingsState settingsState =
649                 new SettingsState(
650                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
651                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
652         final String largeKey = Strings.repeat("A", SettingsState.MAX_LENGTH_PER_STRING + 1);
653         final String testValue = "testValue";
654         synchronized (mLock) {
655             // Test system package
656             try {
657                 settingsState.insertSettingLocked(largeKey, testValue, null, true, SYSTEM_PACKAGE);
658                 fail("Should throw because it exceeded max string length");
659             } catch (IllegalArgumentException ex) {
660                 assertTrue(ex.getMessage().contains("The max length allowed for the string is "));
661             }
662             // Test non system package
663             try {
664                 settingsState.insertSettingLocked(largeKey, testValue, null, true, TEST_PACKAGE);
665                 fail("Should throw because it exceeded max string length");
666             } catch (IllegalArgumentException ex) {
667                 assertTrue(ex.getMessage().contains("The max length allowed for the string is "));
668             }
669         }
670     }
671 
672     @Test
testLargeSettingValue()673     public void testLargeSettingValue() {
674         SettingsState settingsState =
675                 new SettingsState(
676                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
677                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
678         final String testKey = "testKey";
679         final String largeValue = Strings.repeat("A", SettingsState.MAX_LENGTH_PER_STRING + 1);
680         synchronized (mLock) {
681             // Test system package
682             try {
683                 settingsState.insertSettingLocked(testKey, largeValue, null, true, SYSTEM_PACKAGE);
684                 fail("Should throw because it exceeded max string length");
685             } catch (IllegalArgumentException ex) {
686                 assertTrue(ex.getMessage().contains("The max length allowed for the string is "));
687             }
688             // Test non system package
689             try {
690                 settingsState.insertSettingLocked(testKey, largeValue, null, true, TEST_PACKAGE);
691                 fail("Should throw because it exceeded max string length");
692             } catch (IllegalArgumentException ex) {
693                 assertTrue(ex.getMessage().contains("The max length allowed for the string is "));
694             }
695         }
696     }
697 
698     @Test
testApplyStagedConfigValues()699     public void testApplyStagedConfigValues() {
700         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
701         Object lock = new Object();
702         SettingsState settingsState = new SettingsState(
703                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
704                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
705 
706         synchronized (lock) {
707             settingsState.insertSettingLocked(
708                     FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE);
709             settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE);
710             settingsState.persistSettingsLocked();
711         }
712         settingsState.waitForHandler();
713 
714         synchronized (lock) {
715             assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
716             assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
717         }
718 
719         settingsState = new SettingsState(
720                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
721                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
722 
723         synchronized (lock) {
724             assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1).getValue());
725             assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
726 
727             assertEquals(null, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
728         }
729     }
730 
731     @Test
testStagingTransformation()732     public void testStagingTransformation() {
733         assertEquals(INVALID_STAGED_FLAG_1,
734                 SettingsState.createRealFlagName(INVALID_STAGED_FLAG_1));
735         assertEquals(INVALID_STAGED_FLAG_2,
736                 SettingsState.createRealFlagName(INVALID_STAGED_FLAG_2));
737         assertEquals(INVALID_STAGED_FLAG_3,
738                 SettingsState.createRealFlagName(INVALID_STAGED_FLAG_3));
739         assertEquals(INVALID_STAGED_FLAG_4,
740                 SettingsState.createRealFlagName(INVALID_STAGED_FLAG_4));
741 
742         assertEquals(VALID_STAGED_FLAG_1_TRANSFORMED,
743                 SettingsState.createRealFlagName(VALID_STAGED_FLAG_1));
744     }
745 
746     @Test
testInvalidStagedFlagsUnaffectedByReboot()747     public void testInvalidStagedFlagsUnaffectedByReboot() {
748         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
749         Object lock = new Object();
750         SettingsState settingsState = new SettingsState(
751                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
752                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
753 
754         synchronized (lock) {
755             settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1,
756                     VALUE2, null, false, TEST_PACKAGE);
757             settingsState.persistSettingsLocked();
758         }
759         settingsState.waitForHandler();
760         synchronized (lock) {
761             assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
762         }
763 
764         settingsState = new SettingsState(
765                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
766                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
767 
768         synchronized (lock) {
769             assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
770         }
771     }
772 
773     @Test
774     @RequiresFlagsEnabled(Flags.FLAG_STAGE_ALL_ACONFIG_FLAGS)
testSetSettingsLockedStagesAconfigFlags()775     public void testSetSettingsLockedStagesAconfigFlags() throws Exception {
776         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
777 
778         SettingsState settingsState = new SettingsState(
779                 InstrumentationRegistry.getContext(), mLock, mSettingsFile, configKey,
780                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
781 
782         String prefix = "test_namespace";
783         String packageName = "com.android.flags";
784         Map<String, String> keyValues =
785                 Map.of("test_namespace/com.android.flags.flag3", "true");
786 
787         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
788 
789         parsed_flags flags = parsed_flags
790                 .newBuilder()
791                 .addParsedFlag(parsed_flag
792                     .newBuilder()
793                         .setPackage(packageName)
794                         .setName("flag3")
795                         .setNamespace(prefix)
796                         .setDescription("test flag")
797                         .addBug("12345678")
798                         .setState(Aconfig.flag_state.DISABLED)
799                         .setPermission(Aconfig.flag_permission.READ_WRITE))
800                 .build();
801 
802         synchronized (mLock) {
803             settingsState.loadAconfigDefaultValues(
804                     flags.toByteArray(),
805                     settingsState.getAconfigDefaultValues(), flagInfoDefault);
806             List<String> updates =
807                     settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
808             assertEquals(1, updates.size());
809             assertEquals(updates.get(0), "staged/test_namespace*com.android.flags.flag3");
810 
811             SettingsState.Setting s;
812 
813             s = settingsState.getSettingLocked("test_namespace/com.android.flags.flag3");
814             assertNull(s.getValue());
815 
816             s = settingsState.getSettingLocked("staged/test_namespace*com.android.flags.flag3");
817             assertEquals("true", s.getValue());
818         }
819     }
820 
821     @Test
testsetSettingsLockedKeepTrunkDefault()822     public void testsetSettingsLockedKeepTrunkDefault() throws Exception {
823         final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
824         os.print(
825                 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
826                         + "<settings version=\"120\">"
827                         + "  <setting id=\"0\" name=\"test_namespace/flag0\" "
828                             + "value=\"false\" package=\"com.android.flags\" />"
829                         + "  <setting id=\"1\" name=\"test_namespace/flag1\" "
830                             + "value=\"false\" package=\"com.android.flags\" />"
831                         + "  <setting id=\"2\" name=\"test_namespace/com.android.flags.flag3\" "
832                             + "value=\"false\" package=\"com.android.flags\" />"
833                         + "  <setting id=\"3\" "
834                         + "name=\"test_another_namespace/com.android.another.flags.flag0\" "
835                             + "value=\"false\" package=\"com.android.another.flags\" />"
836                         + "</settings>");
837         os.close();
838 
839         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
840 
841         SettingsState settingsState = new SettingsState(
842                 InstrumentationRegistry.getContext(), mLock, mSettingsFile, configKey,
843                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
844 
845         String prefix = "test_namespace";
846         Map<String, String> keyValues =
847                 Map.of("test_namespace/flag0", "true", "test_namespace/flag2", "false");
848         String packageName = "com.android.flags";
849 
850         parsed_flags flags = parsed_flags
851                 .newBuilder()
852                 .addParsedFlag(parsed_flag
853                     .newBuilder()
854                         .setPackage(packageName)
855                         .setName("flag3")
856                         .setNamespace(prefix)
857                         .setDescription("test flag")
858                         .addBug("12345678")
859                         .setState(Aconfig.flag_state.DISABLED)
860                         .setPermission(Aconfig.flag_permission.READ_WRITE))
861                 .addParsedFlag(parsed_flag
862                     .newBuilder()
863                         .setPackage("com.android.another.flags")
864                         .setName("flag0")
865                         .setNamespace("test_another_namespace")
866                         .setDescription("test flag")
867                         .addBug("12345678")
868                         .setState(Aconfig.flag_state.DISABLED)
869                         .setPermission(Aconfig.flag_permission.READ_WRITE))
870                 .build();
871         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
872 
873         synchronized (mLock) {
874             settingsState.loadAconfigDefaultValues(
875                     flags.toByteArray(),
876                     settingsState.getAconfigDefaultValues(),
877                     flagInfoDefault);
878             List<String> updates =
879                     settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
880             assertEquals(3, updates.size());
881 
882             SettingsState.Setting s;
883 
884             s = settingsState.getSettingLocked("test_namespace/flag0");
885             assertEquals("true", s.getValue());
886 
887             s = settingsState.getSettingLocked("test_namespace/flag1");
888             assertNull(s.getValue());
889 
890             s = settingsState.getSettingLocked("test_namespace/flag2");
891             assertEquals("false", s.getValue());
892 
893             s = settingsState.getSettingLocked("test_namespace/com.android.flags.flag3");
894             assertEquals("false", s.getValue());
895 
896             s = settingsState.getSettingLocked(
897                     "test_another_namespace/com.android.another.flags.flag0");
898             assertEquals("false", s.getValue());
899         }
900     }
901 
902     @Test
testsetSettingsLockedNoTrunkDefault()903     public void testsetSettingsLockedNoTrunkDefault() throws Exception {
904         final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
905         os.print(
906                 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
907                         + "<settings version=\"120\">"
908                         + "  <setting id=\"0\" name=\"test_namespace/flag0\" "
909                             + "value=\"false\" package=\"com.android.flags\" />"
910                         + "  <setting id=\"1\" name=\"test_namespace/flag1\" "
911                             + "value=\"false\" package=\"com.android.flags\" />"
912                         + "</settings>");
913         os.close();
914 
915         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
916 
917         SettingsState settingsState = new SettingsState(
918                 InstrumentationRegistry.getContext(), mLock, mSettingsFile, configKey,
919                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
920 
921         Map<String, String> keyValues =
922                 Map.of("test_namespace/flag0", "true", "test_namespace/flag2", "false");
923         String packageName = "com.android.flags";
924 
925         synchronized (mLock) {
926             List<String> updates =
927                     settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
928             assertEquals(3, updates.size());
929 
930             SettingsState.Setting s;
931 
932             s = settingsState.getSettingLocked("test_namespace/flag0");
933             assertEquals("true", s.getValue());
934 
935             s = settingsState.getSettingLocked("test_namespace/flag1");
936             assertNull(s.getValue());
937 
938             s = settingsState.getSettingLocked("test_namespace/flag2");
939             assertEquals("false", s.getValue());
940         }
941     }
942 
943     @Test
testMemoryUsagePerPackage_SameSettingUsedByDifferentPackages()944     public void testMemoryUsagePerPackage_SameSettingUsedByDifferentPackages() {
945         SettingsState settingsState =
946                 new SettingsState(
947                         InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
948                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
949         final String testKey1 = SETTING_NAME;
950         final String testKey2 = SETTING_NAME + "_2";
951         final String testValue1 = Strings.repeat("A", 100);
952         final String testValue2 = Strings.repeat("A", 50);
953         final String package1 = "p1";
954         final String package2 = "p2";
955 
956         settingsState.insertSettingLocked(testKey1, testValue1, null, false, package1);
957         settingsState.insertSettingLocked(testKey2, testValue1, null, true, package2);
958         // Package1's usage should be remain the same Package2 owns a different setting
959         int expectedMemUsageForPackage1 = (testKey1.length() + testValue1.length())
960                 * Character.BYTES;
961         int expectedMemUsageForPackage2 = (testKey2.length() + testValue1.length()
962                 + testValue1.length() /* size for default */) * Character.BYTES;
963         assertEquals(expectedMemUsageForPackage1, settingsState.getMemoryUsage(package1));
964         assertEquals(expectedMemUsageForPackage2, settingsState.getMemoryUsage(package2));
965 
966         settingsState.insertSettingLocked(testKey1, testValue2, null, false, package2);
967         // Package1's usage should be cleared because the setting is taken over by another package
968         expectedMemUsageForPackage1 = 0;
969         assertEquals(expectedMemUsageForPackage1, settingsState.getMemoryUsage(package1));
970         // Package2 now owns two settings
971         expectedMemUsageForPackage2 = (testKey1.length() + testValue2.length()
972                 + testKey2.length() + testValue1.length()
973                 + testValue1.length() /* size for default */)
974                 * Character.BYTES;
975         assertEquals(expectedMemUsageForPackage2, settingsState.getMemoryUsage(package2));
976 
977         settingsState.insertSettingLocked(testKey1, testValue1, null, true, package1);
978         // Package1 now owns setting1
979         expectedMemUsageForPackage1 = (testKey1.length() + testValue1.length()
980                 + testValue1.length() /* size for default */) * Character.BYTES;
981         assertEquals(expectedMemUsageForPackage1, settingsState.getMemoryUsage(package1));
982         // Package2 now only own setting2
983         expectedMemUsageForPackage2 = (testKey2.length() + testValue1.length()
984                 + testValue1.length() /* size for default */) * Character.BYTES;
985         assertEquals(expectedMemUsageForPackage2, settingsState.getMemoryUsage(package2));
986     }
987 
988     @Test
testGetFlagOverrideToSync()989    public void testGetFlagOverrideToSync() {
990         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
991         Object lock = new Object();
992         SettingsState settingsState =
993                 new SettingsState(
994                         InstrumentationRegistry.getContext(),
995                         lock,
996                         mSettingsFile,
997                         configKey,
998                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
999                         Looper.getMainLooper());
1000         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
1001 
1002         // invalid flag name
1003         assertNull(settingsState.getFlagOverrideToSync("invalid_flag", "false", flagInfoDefault));
1004 
1005         // invalid local override flag name
1006         assertNull(
1007                 settingsState.getFlagOverrideToSync(
1008                         "some_namespace/some_flag", "false", flagInfoDefault));
1009 
1010         // not a aconfig flag
1011         assertNull(
1012                 settingsState.getFlagOverrideToSync(
1013                         "some_namespace/some_flag.com", "false", flagInfoDefault));
1014 
1015         AconfigdFlagInfo flag1 =
1016                 AconfigdFlagInfo.newBuilder()
1017                         .setPackageName("com.android.flags")
1018                         .setFlagName("flag1")
1019                         .setDefaultFlagValue("false")
1020                         .setIsReadWrite(true)
1021                         .build();
1022 
1023         flagInfoDefault.put(flag1.getFullFlagName(), flag1);
1024 
1025         // server override
1026 
1027         settingsState.getFlagOverrideToSync(
1028                 "test_namespace/com.android.flags.flag1", "true", flagInfoDefault);
1029         assertEquals("com.android.flags", flag1.getPackageName());
1030         assertEquals("flag1", flag1.getFlagName());
1031         assertEquals("true", flag1.getBootFlagValue());
1032         assertEquals("true", flag1.getServerFlagValue());
1033         assertEquals("false", flag1.getDefaultFlagValue());
1034         assertTrue(flag1.getHasServerOverride());
1035         assertNull(flag1.getLocalFlagValue());
1036 
1037         // local override
1038         settingsState.getFlagOverrideToSync(
1039                 "device_config_overrides/test_namespace:com.android.flags.flag1",
1040                 "false",
1041                 flagInfoDefault);
1042         assertEquals("false", flag1.getBootFlagValue());
1043         assertEquals("false", flag1.getLocalFlagValue());
1044         assertTrue(flag1.getHasLocalOverride());
1045     }
1046 
1047     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
1048 
1049     @Test
1050     @EnableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON)
testHandleBulkSyncWithAconfigdEnabled()1051     public void testHandleBulkSyncWithAconfigdEnabled() {
1052         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
1053         Object lock = new Object();
1054         SettingsState settingsState =
1055                 new SettingsState(
1056                         InstrumentationRegistry.getContext(),
1057                         lock,
1058                         mSettingsFile,
1059                         configKey,
1060                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
1061                         Looper.getMainLooper());
1062 
1063         Map<String, AconfigdFlagInfo> flags = new HashMap<>();
1064         flags.put(
1065                 "com.android.flags/flag1",
1066                 AconfigdFlagInfo.newBuilder()
1067                         .setPackageName("com.android.flags")
1068                         .setFlagName("flag1")
1069                         .setBootFlagValue("true")
1070                         .setIsReadWrite(true)
1071                         .build());
1072 
1073         flags.put(
1074                 "com.android.flags/flag2",
1075                 AconfigdFlagInfo.newBuilder()
1076                         .setPackageName("com.android.flags")
1077                         .setFlagName("flag2")
1078                         .setBootFlagValue("true")
1079                         .setIsReadWrite(false)
1080                         .build());
1081 
1082         synchronized (lock) {
1083             settingsState.insertSettingLocked(
1084                     "aconfigd_marker/bulk_synced", "false", null, false, "aconfig");
1085 
1086             // first bulk sync
1087             ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
1088             assertTrue(requests != null);
1089             String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
1090             assertEquals("true", value);
1091 
1092             // send time should no longer bulk sync
1093             requests = settingsState.handleBulkSyncToNewStorage(flags);
1094             assertTrue(requests == null);
1095             value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
1096             assertEquals("true", value);
1097         }
1098     }
1099 
1100     @Test
1101     @DisableFlags(com.android.aconfig_new_storage.Flags.FLAG_ENABLE_ACONFIG_STORAGE_DAEMON)
testHandleBulkSyncWithAconfigdDisabled()1102     public void testHandleBulkSyncWithAconfigdDisabled() {
1103         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
1104         Object lock = new Object();
1105         SettingsState settingsState = new SettingsState(
1106                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
1107                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
1108 
1109         Map<String, AconfigdFlagInfo> flags = new HashMap<>();
1110         synchronized (lock) {
1111             settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
1112                     "true", null, false, "aconfig");
1113 
1114             // when aconfigd is off, should change the marker to false
1115             ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
1116             assertTrue(requests == null);
1117             String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
1118             assertEquals("false", value);
1119 
1120             // marker started with false value, after call, it should remain false
1121             requests = settingsState.handleBulkSyncToNewStorage(flags);
1122             assertTrue(requests == null);
1123             value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
1124             assertEquals("false", value);
1125         }
1126     }
1127 
1128     @Test
testGetAllAconfigFlagsFromSettings()1129     public void testGetAllAconfigFlagsFromSettings() throws Exception {
1130         final Object lock = new Object();
1131         final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
1132         os.print(
1133                 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
1134                         + "<settings version=\"120\">"
1135                         + "  <setting id=\"0\" name=\"test_namespace/com.android.flags.flag1\" "
1136                             + "value=\"false\" package=\"com.android.flags\" />"
1137                         + "  <setting id=\"1\" name=\"device_config_overrides/test_namespace:com.android.flags.flag1\" "
1138                             + "value=\"true\" package=\"com.android.flags\" />"
1139                         + "  <setting id=\"2\" name=\"device_config_overrides/test_namespace:com.android.flags.flag2\" "
1140                             + "value=\"true\" package=\"com.android.flags\" />"
1141                         + "  <setting id=\"3\" name=\"test_namespace/com.android.flags.flag3\" "
1142                             + "value=\"true\" package=\"com.android.flags\" />"
1143                         + "  <setting id=\"3\" name=\"device_config_overrides/test_namespace:com.android.flags.flag3\" "
1144                             + "value=\"true\" package=\"com.android.flags\" />"
1145                         + "</settings>");
1146         os.close();
1147 
1148         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
1149 
1150         SettingsState settingsState = new SettingsState(
1151                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
1152                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
1153 
1154         int ret;
1155         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
1156         synchronized (lock) {
1157             ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
1158         }
1159         assertEquals(0, ret);
1160 
1161         AconfigdFlagInfo flag1 =
1162                 AconfigdFlagInfo.newBuilder()
1163                         .setPackageName("com.android.flags")
1164                         .setFlagName("flag1")
1165                         .setDefaultFlagValue("false")
1166                         .setIsReadWrite(true)
1167                         .build();
1168         flagInfoDefault.put(flag1.getFullFlagName(), flag1);
1169 
1170         synchronized (lock) {
1171             ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
1172         }
1173         assertEquals(2, ret);
1174         assertEquals("com.android.flags", flag1.getPackageName());
1175         assertEquals("flag1", flag1.getFlagName());
1176         assertEquals("true", flag1.getBootFlagValue());
1177         assertEquals("true", flag1.getLocalFlagValue());
1178         assertEquals("false", flag1.getServerFlagValue());
1179         assertEquals("false", flag1.getDefaultFlagValue());
1180         assertTrue(flag1.getHasServerOverride());
1181         assertTrue(flag1.getHasLocalOverride());
1182 
1183         AconfigdFlagInfo flag2 =
1184                 AconfigdFlagInfo.newBuilder()
1185                         .setPackageName("com.android.flags")
1186                         .setFlagName("flag2")
1187                         .setDefaultFlagValue("false")
1188                         .setIsReadWrite(true)
1189                         .build();
1190         flagInfoDefault.put(flag2.getFullFlagName(), flag2);
1191         synchronized (lock) {
1192             ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
1193         }
1194         assertEquals(3, ret);
1195         assertEquals("com.android.flags", flag2.getPackageName());
1196         assertEquals("flag2", flag2.getFlagName());
1197         assertEquals("true", flag2.getBootFlagValue());
1198         assertEquals("true", flag2.getLocalFlagValue());
1199         assertEquals("false", flag2.getDefaultFlagValue());
1200         assertNull(flag2.getServerFlagValue());
1201         assertFalse(flag2.getHasServerOverride());
1202         assertTrue(flag2.getHasLocalOverride());
1203 
1204         AconfigdFlagInfo flag3 =
1205                 AconfigdFlagInfo.newBuilder()
1206                         .setPackageName("com.android.flags")
1207                         .setFlagName("flag3")
1208                         .setDefaultFlagValue("false")
1209                         .setIsReadWrite(false)
1210                         .build();
1211         flagInfoDefault.put(flag3.getFullFlagName(), flag3);
1212         synchronized (lock) {
1213             ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
1214         }
1215         assertEquals(3, ret);
1216         assertEquals("com.android.flags", flag3.getPackageName());
1217         assertEquals("flag3", flag3.getFlagName());
1218         assertEquals("false", flag3.getBootFlagValue());
1219         assertEquals("false", flag3.getDefaultFlagValue());
1220         assertNull(flag3.getLocalFlagValue());
1221         assertNull(flag3.getServerFlagValue());
1222         assertFalse(flag3.getHasServerOverride());
1223         assertFalse(flag3.getHasLocalOverride());
1224     }
1225 
1226     @Test
testCompareFlagValueInNewStorage()1227     public void testCompareFlagValueInNewStorage() {
1228                 int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
1229         Object lock = new Object();
1230         SettingsState settingsState =
1231                 new SettingsState(
1232                         InstrumentationRegistry.getContext(),
1233                         lock,
1234                         mSettingsFile,
1235                         configKey,
1236                         SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
1237                         Looper.getMainLooper());
1238 
1239         AconfigdFlagInfo defaultFlag1 =
1240                 AconfigdFlagInfo.newBuilder()
1241                         .setPackageName("com.android.flags")
1242                         .setFlagName("flag1")
1243                         .setDefaultFlagValue("false")
1244                         .setServerFlagValue("true")
1245                         .setHasServerOverride(true)
1246                         .setIsReadWrite(true)
1247                         .build();
1248 
1249         AconfigdFlagInfo expectedFlag1 =
1250                 AconfigdFlagInfo.newBuilder()
1251                         .setPackageName("com.android.flags")
1252                         .setFlagName("flag1")
1253                         .setServerFlagValue("true")
1254                         .setDefaultFlagValue("false")
1255                         .setHasServerOverride(true)
1256                         .setIsReadWrite(true)
1257                         .build();
1258 
1259         Map<String, AconfigdFlagInfo> aconfigdMap = new HashMap<>();
1260         Map<String, AconfigdFlagInfo> defaultMap = new HashMap<>();
1261 
1262         defaultMap.put("com.android.flags.flag1", defaultFlag1);
1263         aconfigdMap.put("com.android.flags.flag1", expectedFlag1);
1264 
1265         int ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
1266         assertEquals(0, ret);
1267 
1268         String value =
1269                 settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
1270         assertEquals("0", value);
1271 
1272         AconfigdFlagInfo defaultFlag2 =
1273                 AconfigdFlagInfo.newBuilder()
1274                         .setPackageName("com.android.flags")
1275                         .setFlagName("flag2")
1276                         .setDefaultFlagValue("false")
1277                         .build();
1278         defaultMap.put("com.android.flags.flag2", defaultFlag2);
1279 
1280         ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
1281         // missing from new storage
1282         assertEquals(1, ret);
1283         value =
1284                 settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
1285         assertEquals("1", value);
1286 
1287         AconfigdFlagInfo expectedFlag2 =
1288         AconfigdFlagInfo.newBuilder()
1289                 .setPackageName("com.android.flags")
1290                 .setFlagName("flag2")
1291                 .setServerFlagValue("true")
1292                 .setLocalFlagValue("true")
1293                 .setDefaultFlagValue("false")
1294                 .setHasServerOverride(true)
1295                 .setHasLocalOverride(true)
1296                 .build();
1297         aconfigdMap.put("com.android.flags.flag2", expectedFlag2);
1298         ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
1299         // skip the server and local value comparison when the flag is read_only
1300         assertEquals(0, ret);
1301         value =
1302                 settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
1303         assertEquals("0", value);
1304     }
1305 }
1306