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