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.TypedXmlSerializer; 21 import android.util.Xml; 22 23 import com.google.common.base.Strings; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.File; 27 import java.io.FileOutputStream; 28 import java.io.PrintStream; 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 TypedXmlSerializer serializer = Xml.resolveSerializer(os); 97 serializer.startDocument(null, true); 98 99 for (int ch = 0; ch < 0x10000; ch++) { 100 checkWriteSingleSetting("char=0x" + Integer.toString(ch, 16), serializer, 101 "key", String.valueOf((char) ch)); 102 } 103 checkWriteSingleSetting(serializer, "k", ""); 104 checkWriteSingleSetting(serializer, "x", "abc"); 105 checkWriteSingleSetting(serializer, "abc", CRAZY_STRING); 106 checkWriteSingleSetting(serializer, "def", null); 107 108 // Invlid input, but shouoldn't crash. 109 checkWriteSingleSetting(serializer, null, null); 110 checkWriteSingleSetting(serializer, CRAZY_STRING, null); 111 SettingsState.writeSingleSetting( 112 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 113 serializer, null, "k", "v", null, "package", null, false, false); 114 SettingsState.writeSingleSetting( 115 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 116 serializer, "1", "k", "v", null, null, null, false, false); 117 } 118 checkWriteSingleSetting(TypedXmlSerializer serializer, String key, String value)119 private void checkWriteSingleSetting(TypedXmlSerializer serializer, String key, String value) 120 throws Exception { 121 checkWriteSingleSetting(key + "/" + value, serializer, key, value); 122 } 123 checkWriteSingleSetting(String msg, TypedXmlSerializer serializer, String key, String value)124 private void checkWriteSingleSetting(String msg, TypedXmlSerializer serializer, 125 String key, String value) throws Exception { 126 // Make sure the XML serializer won't crash. 127 SettingsState.writeSingleSetting( 128 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 129 serializer, "1", key, value, null, "package", null, false, false); 130 } 131 132 /** 133 * Make sure settings can be written to a file and also can be read. 134 */ testReadWrite()135 public void testReadWrite() { 136 final File file = new File(getContext().getCacheDir(), "setting.xml"); 137 file.delete(); 138 final Object lock = new Object(); 139 140 final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1, 141 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 142 ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); 143 144 ssWriter.insertSettingLocked("k1", "\u0000", null, false, "package"); 145 ssWriter.insertSettingLocked("k2", "abc", null, false, "p2"); 146 ssWriter.insertSettingLocked("k3", null, null, false, "p2"); 147 ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3"); 148 synchronized (lock) { 149 ssWriter.persistSyncLocked(); 150 } 151 152 final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1, 153 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 154 synchronized (lock) { 155 assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue()); 156 assertEquals("abc", ssReader.getSettingLocked("k2").getValue()); 157 assertEquals(null, ssReader.getSettingLocked("k3").getValue()); 158 assertEquals(CRAZY_STRING, ssReader.getSettingLocked("k4").getValue()); 159 } 160 } 161 162 /** 163 * In version 120, value "null" meant {code NULL}. 164 */ testUpgrade()165 public void testUpgrade() throws Exception { 166 final File file = new File(getContext().getCacheDir(), "setting.xml"); 167 file.delete(); 168 final Object lock = new Object(); 169 final PrintStream os = new PrintStream(new FileOutputStream(file)); 170 os.print( 171 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" + 172 "<settings version=\"120\">" + 173 " <setting id=\"0\" name=\"k0\" value=\"null\" package=\"null\" />" + 174 " <setting id=\"1\" name=\"k1\" value=\"\" package=\"\" />" + 175 " <setting id=\"2\" name=\"k2\" value=\"v2\" package=\"p2\" />" + 176 "</settings>"); 177 os.close(); 178 179 final SettingsState ss = new SettingsState(getContext(), lock, file, 1, 180 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 181 synchronized (lock) { 182 SettingsState.Setting s; 183 s = ss.getSettingLocked("k0"); 184 assertEquals(null, s.getValue()); 185 assertEquals("null", s.getPackageName()); 186 187 s = ss.getSettingLocked("k1"); 188 assertEquals("", s.getValue()); 189 assertEquals("", s.getPackageName()); 190 191 s = ss.getSettingLocked("k2"); 192 assertEquals("v2", s.getValue()); 193 assertEquals("p2", s.getPackageName()); 194 } 195 } 196 testInitializeSetting_preserveFlagNotSet()197 public void testInitializeSetting_preserveFlagNotSet() { 198 SettingsState settingsWriter = getSettingStateObject(); 199 settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 200 settingsWriter.persistSyncLocked(); 201 202 SettingsState settingsReader = getSettingStateObject(); 203 assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 204 } 205 testModifySetting_preserveFlagSet()206 public void testModifySetting_preserveFlagSet() { 207 SettingsState settingsWriter = getSettingStateObject(); 208 settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 209 settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE); 210 settingsWriter.persistSyncLocked(); 211 212 SettingsState settingsReader = getSettingStateObject(); 213 assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 214 } 215 testModifySettingOverrideableByRestore_preserveFlagNotSet()216 public void testModifySettingOverrideableByRestore_preserveFlagNotSet() { 217 SettingsState settingsWriter = getSettingStateObject(); 218 settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 219 settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE, 220 /* overrideableByRestore */ true); 221 settingsWriter.persistSyncLocked(); 222 223 SettingsState settingsReader = getSettingStateObject(); 224 assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 225 } 226 testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged()227 public void testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged() { 228 SettingsState settingsWriter = getSettingStateObject(); 229 // Init the setting. 230 settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 231 // This modification will set isValuePreservedInRestore = true. 232 settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 233 // This modification shouldn't change the value of isValuePreservedInRestore since it's 234 // already been set to true. 235 settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE, 236 /* overrideableByRestore */ true); 237 settingsWriter.persistSyncLocked(); 238 239 SettingsState settingsReader = getSettingStateObject(); 240 assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 241 } 242 testResetSetting_preservedFlagIsReset()243 public void testResetSetting_preservedFlagIsReset() { 244 SettingsState settingsState = getSettingStateObject(); 245 // Initialize the setting. 246 settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE); 247 // Update the setting so that preserved flag is set. 248 settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE); 249 250 settingsState.resetSettingLocked(SETTING_NAME); 251 assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 252 253 } 254 testModifySettingBySystemPackage_sameValue_preserveFlagNotSet()255 public void testModifySettingBySystemPackage_sameValue_preserveFlagNotSet() { 256 SettingsState settingsState = getSettingStateObject(); 257 // Initialize the setting. 258 settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); 259 // Update the setting. 260 settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); 261 262 assertFalse(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 263 } 264 testModifySettingBySystemPackage_newValue_preserveFlagSet()265 public void testModifySettingBySystemPackage_newValue_preserveFlagSet() { 266 SettingsState settingsState = getSettingStateObject(); 267 // Initialize the setting. 268 settingsState.insertSettingLocked(SETTING_NAME, "1", null, false, SYSTEM_PACKAGE); 269 // Update the setting. 270 settingsState.insertSettingLocked(SETTING_NAME, "2", null, false, SYSTEM_PACKAGE); 271 272 assertTrue(settingsState.getSettingLocked(SETTING_NAME).isValuePreservedInRestore()); 273 } 274 getSettingStateObject()275 private SettingsState getSettingStateObject() { 276 SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, 277 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 278 settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); 279 return settingsState; 280 } 281 testInsertSetting_memoryUsage()282 public void testInsertSetting_memoryUsage() { 283 SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, 284 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 285 // No exception should be thrown when there is no cap 286 settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), 287 null, false, "p1"); 288 settingsState.deleteSettingLocked(SETTING_NAME); 289 290 settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, 291 SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); 292 // System package doesn't have memory usage limit 293 settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), 294 null, false, SYSTEM_PACKAGE); 295 settingsState.deleteSettingLocked(SETTING_NAME); 296 297 // Should not throw if usage is under the cap 298 settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975), 299 null, false, "p1"); 300 settingsState.deleteSettingLocked(SETTING_NAME); 301 try { 302 settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), 303 null, false, "p1"); 304 fail("Should throw because it exceeded per package memory usage"); 305 } catch (IllegalStateException ex) { 306 assertTrue(ex.getMessage().contains("p1")); 307 } 308 try { 309 settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), 310 null, false, "p1"); 311 fail("Should throw because it exceeded per package memory usage"); 312 } catch (IllegalStateException ex) { 313 assertTrue(ex.getMessage().contains("p1")); 314 } 315 assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); 316 try { 317 settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", 318 null, false, "p1"); 319 fail("Should throw because it exceeded per package memory usage"); 320 } catch (IllegalStateException ex) { 321 assertTrue(ex.getMessage().contains("You are adding too many system settings")); 322 } 323 } 324 testMemoryUsagePerPackage()325 public void testMemoryUsagePerPackage() { 326 SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, 327 SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); 328 329 // Test inserting one key with default 330 final String testKey1 = SETTING_NAME; 331 final String testValue1 = Strings.repeat("A", 100); 332 settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE); 333 int expectedMemUsage = testKey1.length() + testValue1.length() 334 + testValue1.length() /* size for default */; 335 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 336 337 // Test inserting another key 338 final String testKey2 = SETTING_NAME + "2"; 339 settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE); 340 expectedMemUsage += testKey2.length() + testValue1.length(); 341 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 342 343 // Test updating first key with new default 344 final String testValue2 = Strings.repeat("A", 300); 345 settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE); 346 expectedMemUsage += (testValue2.length() - testValue1.length()) * 2; 347 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 348 349 // Test updating first key without new default 350 final String testValue3 = Strings.repeat("A", 50); 351 settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE); 352 expectedMemUsage -= testValue2.length() - testValue3.length(); 353 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 354 355 // Test updating second key 356 settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE); 357 expectedMemUsage -= testValue1.length() - testValue2.length(); 358 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 359 360 // Test resetting key 361 settingsState.resetSettingLocked(testKey1); 362 expectedMemUsage += testValue2.length() - testValue3.length(); 363 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 364 365 // Test resetting default value 366 settingsState.resetSettingDefaultValueLocked(testKey1); 367 expectedMemUsage -= testValue2.length(); 368 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 369 370 // Test deletion 371 settingsState.deleteSettingLocked(testKey2); 372 expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */; 373 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 374 375 // Test another package with a different key 376 final String testPackage2 = TEST_PACKAGE + "2"; 377 final String testKey3 = SETTING_NAME + "3"; 378 settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2); 379 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 380 final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2; 381 assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); 382 383 // Test system package 384 settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE); 385 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 386 assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); 387 assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE)); 388 389 // Test invalid value 390 try { 391 settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false, 392 TEST_PACKAGE); 393 fail("Should throw because it exceeded per package memory usage"); 394 } catch (IllegalStateException ex) { 395 assertTrue(ex.getMessage().contains("You are adding too many system settings")); 396 } 397 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 398 399 // Test invalid key 400 try { 401 settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false, 402 TEST_PACKAGE); 403 fail("Should throw because it exceeded per package memory usage"); 404 } catch (IllegalStateException ex) { 405 assertTrue(ex.getMessage().contains("You are adding too many system settings")); 406 } 407 assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); 408 } 409 } 410