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 testIsBinary()50 public void testIsBinary() { 51 assertFalse(SettingsState.isBinary(" abc 日本語")); 52 53 for (char ch = 0x20; ch < 0xd800; ch++) { 54 assertFalse("ch=" + Integer.toString(ch, 16), 55 SettingsState.isBinary(String.valueOf(ch))); 56 } 57 for (char ch = 0xe000; ch < 0xfffe; ch++) { 58 assertFalse("ch=" + Integer.toString(ch, 16), 59 SettingsState.isBinary(String.valueOf(ch))); 60 } 61 62 for (char ch = 0x0000; ch < 0x20; ch++) { 63 assertTrue("ch=" + Integer.toString(ch, 16), 64 SettingsState.isBinary(String.valueOf(ch))); 65 } 66 for (char ch = 0xd800; ch < 0xe000; ch++) { 67 assertTrue("ch=" + Integer.toString(ch, 16), 68 SettingsState.isBinary(String.valueOf(ch))); 69 } 70 assertTrue(SettingsState.isBinary("\ufffe")); 71 assertTrue(SettingsState.isBinary("\uffff")); 72 try { 73 assertFalse(SettingsState.isBinary(null)); 74 fail("NullPointerException expected"); 75 } catch (NullPointerException expected) { 76 } 77 } 78 79 /** Make sure we won't pass invalid characters to XML serializer. */ testWriteReadNoCrash()80 public void testWriteReadNoCrash() throws Exception { 81 ByteArrayOutputStream os = new ByteArrayOutputStream(); 82 83 XmlSerializer serializer = Xml.newSerializer(); 84 serializer.setOutput(os, StandardCharsets.UTF_8.name()); 85 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 86 serializer.startDocument(null, true); 87 88 for (int ch = 0; ch < 0x10000; ch++) { 89 checkWriteSingleSetting("char=0x" + Integer.toString(ch, 16), serializer, 90 "key", String.valueOf((char) ch)); 91 } 92 checkWriteSingleSetting(serializer, "k", ""); 93 checkWriteSingleSetting(serializer, "x", "abc"); 94 checkWriteSingleSetting(serializer, "abc", CRAZY_STRING); 95 checkWriteSingleSetting(serializer, "def", null); 96 97 // Invlid input, but shouoldn't crash. 98 checkWriteSingleSetting(serializer, null, null); 99 checkWriteSingleSetting(serializer, CRAZY_STRING, null); 100 SettingsState.writeSingleSetting( 101 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 102 serializer, null, "k", "v", null, "package", null, false); 103 SettingsState.writeSingleSetting( 104 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 105 serializer, "1", "k", "v", null, null, null, false); 106 } 107 checkWriteSingleSetting(XmlSerializer serializer, String key, String value)108 private void checkWriteSingleSetting(XmlSerializer serializer, String key, String value) 109 throws Exception { 110 checkWriteSingleSetting(key + "/" + value, serializer, key, value); 111 } 112 checkWriteSingleSetting(String msg, XmlSerializer serializer, String key, String value)113 private void checkWriteSingleSetting(String msg, XmlSerializer serializer, 114 String key, String value) throws Exception { 115 // Make sure the XML serializer won't crash. 116 SettingsState.writeSingleSetting( 117 SettingsState.SETTINGS_VERSION_NEW_ENCODING, 118 serializer, "1", key, value, null, "package", null, false); 119 } 120 121 /** 122 * Make sure settings can be written to a file and also can be read. 123 */ testReadWrite()124 public void testReadWrite() { 125 final File file = new File(getContext().getCacheDir(), "setting.xml"); 126 file.delete(); 127 final Object lock = new Object(); 128 129 final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1, 130 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 131 ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); 132 133 ssWriter.insertSettingLocked("k1", "\u0000", null, false, "package"); 134 ssWriter.insertSettingLocked("k2", "abc", null, false, "p2"); 135 ssWriter.insertSettingLocked("k3", null, null, false, "p2"); 136 ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3"); 137 synchronized (lock) { 138 ssWriter.persistSyncLocked(); 139 } 140 141 final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1, 142 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 143 synchronized (lock) { 144 assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue()); 145 assertEquals("abc", ssReader.getSettingLocked("k2").getValue()); 146 assertEquals(null, ssReader.getSettingLocked("k3").getValue()); 147 assertEquals(CRAZY_STRING, ssReader.getSettingLocked("k4").getValue()); 148 } 149 } 150 151 /** 152 * In version 120, value "null" meant {code NULL}. 153 */ testUpgrade()154 public void testUpgrade() throws Exception { 155 final File file = new File(getContext().getCacheDir(), "setting.xml"); 156 file.delete(); 157 final Object lock = new Object(); 158 final PrintStream os = new PrintStream(new FileOutputStream(file)); 159 os.print( 160 "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" + 161 "<settings version=\"120\">" + 162 " <setting id=\"0\" name=\"k0\" value=\"null\" package=\"null\" />" + 163 " <setting id=\"1\" name=\"k1\" value=\"\" package=\"\" />" + 164 " <setting id=\"2\" name=\"k2\" value=\"v2\" package=\"p2\" />" + 165 "</settings>"); 166 os.close(); 167 168 final SettingsState ss = new SettingsState(getContext(), lock, file, 1, 169 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); 170 synchronized (lock) { 171 SettingsState.Setting s; 172 s = ss.getSettingLocked("k0"); 173 assertEquals(null, s.getValue()); 174 assertEquals("null", s.getPackageName()); 175 176 s = ss.getSettingLocked("k1"); 177 assertEquals("", s.getValue()); 178 assertEquals("", s.getPackageName()); 179 180 s = ss.getSettingLocked("k2"); 181 assertEquals("v2", s.getValue()); 182 assertEquals("p2", s.getPackageName()); 183 } 184 } 185 } 186