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 android.os.Looper;
19 import android.test.AndroidTestCase;
20 import android.util.Xml;
21 
22 import org.xmlpull.v1.XmlSerializer;
23 
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.PrintStream;
28 import java.nio.charset.StandardCharsets;
29 
30 public class SettingsStateTest extends AndroidTestCase {
31     public static final String CRAZY_STRING =
32             "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000b\u000c\r" +
33             "\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a" +
34             "\u001b\u001c\u001d\u001e\u001f\u0020" +
35             "fake_setting_value_1" +
36             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
37             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
38             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
39             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
40             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
41             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
42             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
43             "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
44             "\u1000 \u2000 \u5000 \u8000 \uc000 \ue000" +
45             "\ud800\udc00\udbff\udfff" + // surrogate pairs
46             "\uD800ab\uDC00 " + // broken surrogate pairs
47             "日本語";
48 
49     private static final String TEST_PACKAGE = "package";
50     private static final String SYSTEM_PACKAGE = "android";
51     private static final String SETTING_NAME = "test_setting";
52 
53     private final Object mLock = new Object();
54 
55     private File mSettingsFile;
56 
57     @Override
setUp()58     protected void setUp() {
59         mSettingsFile = new File(getContext().getCacheDir(), "setting.xml");
60         mSettingsFile.delete();
61     }
62 
testIsBinary()63     public void testIsBinary() {
64         assertFalse(SettingsState.isBinary(" abc 日本語"));
65 
66         for (char ch = 0x20; ch < 0xd800; ch++) {
67             assertFalse("ch=" + Integer.toString(ch, 16),
68                     SettingsState.isBinary(String.valueOf(ch)));
69         }
70         for (char ch = 0xe000; ch < 0xfffe; ch++) {
71             assertFalse("ch=" + Integer.toString(ch, 16),
72                     SettingsState.isBinary(String.valueOf(ch)));
73         }
74 
75         for (char ch = 0x0000; ch < 0x20; ch++) {
76             assertTrue("ch=" + Integer.toString(ch, 16),
77                     SettingsState.isBinary(String.valueOf(ch)));
78         }
79         for (char ch = 0xd800; ch < 0xe000; ch++) {
80             assertTrue("ch=" + Integer.toString(ch, 16),
81                     SettingsState.isBinary(String.valueOf(ch)));
82         }
83         assertTrue(SettingsState.isBinary("\ufffe"));
84         assertTrue(SettingsState.isBinary("\uffff"));
85         try {
86             assertFalse(SettingsState.isBinary(null));
87             fail("NullPointerException expected");
88         } catch (NullPointerException expected) {
89         }
90     }
91 
92     /** Make sure we won't pass invalid characters to XML serializer. */
testWriteReadNoCrash()93     public void testWriteReadNoCrash() throws Exception {
94         ByteArrayOutputStream os = new ByteArrayOutputStream();
95 
96         XmlSerializer serializer = Xml.newSerializer();
97         serializer.setOutput(os, StandardCharsets.UTF_8.name());
98         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
99         serializer.startDocument(null, true);
100 
101         for (int ch = 0; ch < 0x10000; ch++) {
102             checkWriteSingleSetting("char=0x" + Integer.toString(ch, 16), serializer,
103                     "key", String.valueOf((char) ch));
104         }
105         checkWriteSingleSetting(serializer, "k", "");
106         checkWriteSingleSetting(serializer, "x", "abc");
107         checkWriteSingleSetting(serializer, "abc", CRAZY_STRING);
108         checkWriteSingleSetting(serializer, "def", null);
109 
110         // Invlid input, but shouoldn't crash.
111         checkWriteSingleSetting(serializer, null, null);
112         checkWriteSingleSetting(serializer, CRAZY_STRING, null);
113         SettingsState.writeSingleSetting(
114                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
115                 serializer, null, "k", "v", null, "package", null, false, false);
116         SettingsState.writeSingleSetting(
117                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
118                 serializer, "1", "k", "v", null, null, null, false, false);
119     }
120 
checkWriteSingleSetting(XmlSerializer serializer, String key, String value)121     private void checkWriteSingleSetting(XmlSerializer serializer, String key, String value)
122             throws Exception {
123         checkWriteSingleSetting(key + "/" + value, serializer, key, value);
124     }
125 
checkWriteSingleSetting(String msg, XmlSerializer serializer, String key, String value)126     private void checkWriteSingleSetting(String msg, XmlSerializer serializer,
127             String key, String value) throws Exception {
128         // Make sure the XML serializer won't crash.
129         SettingsState.writeSingleSetting(
130                 SettingsState.SETTINGS_VERSION_NEW_ENCODING,
131                 serializer, "1", key, value, null, "package", null, false, false);
132     }
133 
134     /**
135      * Make sure settings can be written to a file and also can be read.
136      */
testReadWrite()137     public void testReadWrite() {
138         final File file = new File(getContext().getCacheDir(), "setting.xml");
139         file.delete();
140         final Object lock = new Object();
141 
142         final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1,
143                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
144         ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
145 
146         ssWriter.insertSettingLocked("k1", "\u0000", null, false, "package");
147         ssWriter.insertSettingLocked("k2", "abc", null, false, "p2");
148         ssWriter.insertSettingLocked("k3", null, null, false, "p2");
149         ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3");
150         synchronized (lock) {
151             ssWriter.persistSyncLocked();
152         }
153 
154         final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1,
155                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
156         synchronized (lock) {
157             assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue());
158             assertEquals("abc", ssReader.getSettingLocked("k2").getValue());
159             assertEquals(null, ssReader.getSettingLocked("k3").getValue());
160             assertEquals(CRAZY_STRING, ssReader.getSettingLocked("k4").getValue());
161         }
162     }
163 
164     /**
165      * In version 120, value "null" meant {code NULL}.
166      */
testUpgrade()167     public void testUpgrade() throws Exception {
168         final File file = new File(getContext().getCacheDir(), "setting.xml");
169         file.delete();
170         final Object lock = new Object();
171         final PrintStream os = new PrintStream(new FileOutputStream(file));
172         os.print(
173                 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" +
174                 "<settings version=\"120\">" +
175                 "  <setting id=\"0\" name=\"k0\" value=\"null\" package=\"null\" />" +
176                 "  <setting id=\"1\" name=\"k1\" value=\"\" package=\"\" />" +
177                 "  <setting id=\"2\" name=\"k2\" value=\"v2\" package=\"p2\" />" +
178                 "</settings>");
179         os.close();
180 
181         final SettingsState ss = new SettingsState(getContext(), lock, file, 1,
182                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
183         synchronized (lock) {
184             SettingsState.Setting s;
185             s = ss.getSettingLocked("k0");
186             assertEquals(null, s.getValue());
187             assertEquals("null", s.getPackageName());
188 
189             s = ss.getSettingLocked("k1");
190             assertEquals("", s.getValue());
191             assertEquals("", s.getPackageName());
192 
193             s = ss.getSettingLocked("k2");
194             assertEquals("v2", s.getValue());
195             assertEquals("p2", s.getPackageName());
196         }
197     }
198 
testInitializeSetting_preserveFlagNotSet()199     public void testInitializeSetting_preserveFlagNotSet() {
200         SettingsState settingsWriter = getSettingStateObject();
201         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
202         settingsWriter.persistSyncLocked();
203 
204         SettingsState settingsReader = getSettingStateObject();
205         assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
206     }
207 
testModifySetting_preserveFlagSet()208     public void testModifySetting_preserveFlagSet() {
209         SettingsState settingsWriter = getSettingStateObject();
210         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
211         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
212         settingsWriter.persistSyncLocked();
213 
214         SettingsState settingsReader = getSettingStateObject();
215         assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
216     }
217 
testModifySettingOverrideableByRestore_preserveFlagNotSet()218     public void testModifySettingOverrideableByRestore_preserveFlagNotSet() {
219         SettingsState settingsWriter = getSettingStateObject();
220         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
221         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
222                 /* overrideableByRestore */ true);
223         settingsWriter.persistSyncLocked();
224 
225         SettingsState settingsReader = getSettingStateObject();
226         assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
227     }
228 
testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged()229     public void testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged() {
230         SettingsState settingsWriter = getSettingStateObject();
231         // Init the setting.
232         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
233         // This modification will set isValuePreservedInRestore = true.
234         settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
235         // This modification shouldn't change the value of isValuePreservedInRestore since it's
236         // already been set to true.
237         settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
238                 /* overrideableByRestore */ true);
239         settingsWriter.persistSyncLocked();
240 
241         SettingsState settingsReader = getSettingStateObject();
242         assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
243     }
244 
testResetSetting_preservedFlagIsReset()245     public void testResetSetting_preservedFlagIsReset() {
246         SettingsState settingsState = getSettingStateObject();
247         // Initialize the setting.
248         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
249         // Update the setting so that preserved flag is set.
250         settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
251 
252         settingsState.resetSettingLocked(SETTING_NAME);
253         assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
254 
255     }
256 
testModifySettingBySystemPackage_sameValue_preserveFlagNotSet()257     public void testModifySettingBySystemPackage_sameValue_preserveFlagNotSet() {
258         SettingsState settingsState = getSettingStateObject();
259         // Initialize the setting.
260         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
261         // Update the setting.
262         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
263 
264         assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
265     }
266 
testModifySettingBySystemPackage_newValue_preserveFlagSet()267     public void testModifySettingBySystemPackage_newValue_preserveFlagSet() {
268         SettingsState settingsState = getSettingStateObject();
269         // Initialize the setting.
270         settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE);
271         // Update the setting.
272         settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, SYSTEM_PACKAGE);
273 
274         assertTrue(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
275     }
276 
getSettingStateObject()277     private SettingsState getSettingStateObject() {
278         SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
279                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
280         settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
281         return settingsState;
282     }
283 }
284