1683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav/* 2683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * Copyright (C) 2015 The Android Open Source Project 3683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * 4683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * Licensed under the Apache License, Version 2.0 (the "License"); 5683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * you may not use this file except in compliance with the License. 6683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * You may obtain a copy of the License at 7683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * 8683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * http://www.apache.org/licenses/LICENSE-2.0 9683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * 10683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * Unless required by applicable law or agreed to in writing, software 11683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * distributed under the License is distributed on an "AS IS" BASIS, 12683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * See the License for the specific language governing permissions and 14683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * limitations under the License. 15683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav */ 16683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 17683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavpackage com.android.providers.settings; 18683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 19683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.os.Handler; 20683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.os.Message; 21683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.os.SystemClock; 22683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.provider.Settings; 23683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.text.TextUtils; 24683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.util.ArrayMap; 25683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.util.AtomicFile; 263a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onukiimport android.util.Base64; 27683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.util.Slog; 28683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.util.Xml; 29683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport com.android.internal.annotations.GuardedBy; 30e1519582ab3010a132d4c4f1fa5cc92fad7cbc39Svetoslav Ganovimport com.android.internal.os.BackgroundThread; 31683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport libcore.io.IoUtils; 32683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport libcore.util.Objects; 33683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport org.xmlpull.v1.XmlPullParser; 34683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport org.xmlpull.v1.XmlPullParserException; 35683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport org.xmlpull.v1.XmlSerializer; 36683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 37683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.File; 38683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.FileInputStream; 39683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.FileNotFoundException; 40683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.FileOutputStream; 41683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.IOException; 429e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets; 43683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.util.ArrayList; 44683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.util.List; 45683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 46683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav/** 47683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * This class contains the state for one type of settings. It is responsible 48683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * for saving the state asynchronously to an XML file after a mutation and 49683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * loading the from an XML file on construction. 50683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * <p> 51683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * This class uses the same lock as the settings provider to ensure that 52683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * multiple changes made by the settings provider, e,g, upgrade, bulk insert, 53683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * etc, are atomically persisted since the asynchronous persistence is using 54683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * the same lock to grab the current state to write to disk. 55683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav * </p> 56683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav */ 57683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavfinal class SettingsState { 58683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final boolean DEBUG = false; 59683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final boolean DEBUG_PERSISTENCE = false; 60683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 61683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String LOG_TAG = "SettingsState"; 62683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 633a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki static final int SETTINGS_VERSOIN_NEW_ENCODING = 121; 643a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 65683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; 66683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; 67683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 68683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public static final int MAX_BYTES_PER_APP_PACKAGE_UNLIMITED = -1; 69683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public static final int MAX_BYTES_PER_APP_PACKAGE_LIMITED = 20000; 70683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 71683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public static final String SYSTEM_PACKAGE_NAME = "android"; 72683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 73683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public static final int VERSION_UNDEFINED = -1; 74683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 75683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String TAG_SETTINGS = "settings"; 76683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String TAG_SETTING = "setting"; 77683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTR_PACKAGE = "package"; 78683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 79683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTR_VERSION = "version"; 80683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTR_ID = "id"; 81683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTR_NAME = "name"; 823a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 833a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki /** Non-binary value will be written in this attribute. */ 84683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTR_VALUE = "value"; 85683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 863a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki /** 873a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki * KXmlSerializer won't like some characters. We encode such characters in base64 and 883a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki * store in this attribute. 893a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki * NOTE: A null value will have NEITHER ATTR_VALUE nor ATTR_VALUE_BASE64. 903a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki */ 913a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static final String ATTR_VALUE_BASE64 = "valueBase64"; 923a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 933a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // This was used in version 120 and before. 943a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static final String NULL_VALUE_OLD_STYLE = "null"; 95683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 96683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final Object mLock; 97683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 98683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final Handler mHandler = new MyHandler(); 99683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 100683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 101683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final ArrayMap<String, Setting> mSettings = new ArrayMap<>(); 102683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 103683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 104683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final ArrayMap<String, Integer> mPackageToMemoryUsage; 105683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 106683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 107683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final int mMaxBytesPerAppPackage; 108683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 109683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 110683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final File mStatePersistFile; 111683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 112683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public final int mKey; 113683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 114683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 115683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private int mVersion = VERSION_UNDEFINED; 116683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 117683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 118683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private long mLastNotWrittenMutationTimeMillis; 119683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 120683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 121683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private boolean mDirty; 122683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 123683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @GuardedBy("mLock") 124683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private boolean mWriteScheduled; 125683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 126b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav @GuardedBy("mLock") 127b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav private long mNextId; 128b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav 129683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public SettingsState(Object lock, File file, int key, int maxBytesPerAppPackage) { 130683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // It is important that we use the same lock as the settings provider 131683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // to ensure multiple mutations on this state are atomicaly persisted 132683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // as the async persistence should be blocked while we make changes. 133683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mLock = lock; 134683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mStatePersistFile = file; 135683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mKey = key; 136683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (maxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_LIMITED) { 137683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mMaxBytesPerAppPackage = maxBytesPerAppPackage; 138683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mPackageToMemoryUsage = new ArrayMap<>(); 139683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } else { 140683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mMaxBytesPerAppPackage = maxBytesPerAppPackage; 141683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mPackageToMemoryUsage = null; 142683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 143683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav synchronized (mLock) { 144683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav readStateSyncLocked(); 145683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 146683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 147683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 148683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 149683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public int getVersionLocked() { 150683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return mVersion; 151683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 152683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 153683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 154683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public void setVersionLocked(int version) { 155683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (version == mVersion) { 156683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 157683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 158683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mVersion = version; 159683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 160683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav scheduleWriteIfNeededLocked(); 161683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 162683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 163683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 164683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public void onPackageRemovedLocked(String packageName) { 165683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav boolean removedSomething = false; 166683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 167683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int settingCount = mSettings.size(); 168683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav for (int i = settingCount - 1; i >= 0; i--) { 169683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String name = mSettings.keyAt(i); 1708de348095f0967ffaa09d2448dc1ccff72a4e284Svet Ganov // Settings defined by us are never dropped. 171683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (Settings.System.PUBLIC_SETTINGS.contains(name) 172683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav || Settings.System.PRIVATE_SETTINGS.contains(name)) { 173683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav continue; 174683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 175683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Setting setting = mSettings.valueAt(i); 176683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (packageName.equals(setting.packageName)) { 177683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mSettings.removeAt(i); 178683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav removedSomething = true; 179683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 180683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 181683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 182683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (removedSomething) { 183683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav scheduleWriteIfNeededLocked(); 184683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 185683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 186683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 187683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 188683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public List<String> getSettingNamesLocked() { 189683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ArrayList<String> names = new ArrayList<>(); 190683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int settingsCount = mSettings.size(); 191683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav for (int i = 0; i < settingsCount; i++) { 192683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String name = mSettings.keyAt(i); 193683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav names.add(name); 194683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 195683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return names; 196683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 197683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 198683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 199683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public Setting getSettingLocked(String name) { 200683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (TextUtils.isEmpty(name)) { 201683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return null; 202683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 203683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return mSettings.get(name); 204683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 205683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 206683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 207683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public boolean updateSettingLocked(String name, String value, String packageName) { 208683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (!hasSettingLocked(name)) { 209683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return false; 210683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 211683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 212683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return insertSettingLocked(name, value, packageName); 213683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 214683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 215683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 216683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public boolean insertSettingLocked(String name, String value, String packageName) { 217683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (TextUtils.isEmpty(name)) { 218683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return false; 219683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 220683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 221683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Setting oldState = mSettings.get(name); 222683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String oldValue = (oldState != null) ? oldState.value : null; 223683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 224683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (oldState != null) { 225683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (!oldState.update(value, packageName)) { 226683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return false; 227683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 228683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } else { 229683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Setting state = new Setting(name, value, packageName); 230683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mSettings.put(name, state); 231683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 232683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 233683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav updateMemoryUsagePerPackageLocked(packageName, oldValue, value); 234683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 235683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav scheduleWriteIfNeededLocked(); 236683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 237683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return true; 238683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 239683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 240683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 241683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public void persistSyncLocked() { 242683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 243683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav doWriteState(); 244683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 245683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 246683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 247683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public boolean deleteSettingLocked(String name) { 248683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) { 249683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return false; 250683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 251683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 252683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Setting oldState = mSettings.remove(name); 253683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 254683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, null); 255683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 256683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav scheduleWriteIfNeededLocked(); 257683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 258683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return true; 259683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 260683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 261683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // The settings provider must hold its lock when calling here. 262683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public void destroyLocked(Runnable callback) { 263683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 264683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (callback != null) { 265683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (mDirty) { 266683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // Do it without a delay. 267683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS, 268683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav callback).sendToTarget(); 269683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 270683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 271683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav callback.run(); 272683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 273683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 274683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 275683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, 276683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String newValue) { 277683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { 278683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 279683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 280683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 281683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (SYSTEM_PACKAGE_NAME.equals(packageName)) { 282683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 283683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 284683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 285683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; 286683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int newValueSize = (newValue != null) ? newValue.length() : 0; 287683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int deltaSize = newValueSize - oldValueSize; 288683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 289683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Integer currentSize = mPackageToMemoryUsage.get(packageName); 290683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int newSize = Math.max((currentSize != null) 291683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ? currentSize + deltaSize : deltaSize, 0); 292683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 293683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (newSize > mMaxBytesPerAppPackage) { 294683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav throw new IllegalStateException("You are adding too many system settings. " 2952849465ee19febd5135cb6ab8cb548a3c8ac6a24Svetoslav + "You should stop using system settings for app specific data" 2962849465ee19febd5135cb6ab8cb548a3c8ac6a24Svetoslav + " package: " + packageName); 297683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 298683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 299683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (DEBUG) { 300683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Slog.i(LOG_TAG, "Settings for package: " + packageName 301683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav + " size: " + newSize + " bytes."); 302683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 303683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 304683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mPackageToMemoryUsage.put(packageName, newSize); 305683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 306683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 307683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private boolean hasSettingLocked(String name) { 308683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return mSettings.indexOfKey(name) >= 0; 309683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 310683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 311683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void scheduleWriteIfNeededLocked() { 312683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // If dirty then we have a write already scheduled. 313683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (!mDirty) { 314683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mDirty = true; 315683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav writeStateAsyncLocked(); 316683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 317683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 318683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 319683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void writeStateAsyncLocked() { 320683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final long currentTimeMillis = SystemClock.uptimeMillis(); 321683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 322683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (mWriteScheduled) { 323683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS); 324683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 325683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // If enough time passed, write without holding off anymore. 326683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis 327683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav - mLastNotWrittenMutationTimeMillis; 328683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) { 329683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget(); 330683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 331683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 332683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 333683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav // Hold off a bit more as settings are frequently changing. 334683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis 335683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0); 336683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis); 337683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 338683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 339683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.sendMessageDelayed(message, writeDelayMillis); 340683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } else { 341683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mLastNotWrittenMutationTimeMillis = currentTimeMillis; 342683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS); 343683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mHandler.sendMessageDelayed(message, WRITE_SETTINGS_DELAY_MILLIS); 344683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mWriteScheduled = true; 345683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 346683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 347683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 348683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void doWriteState() { 349683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (DEBUG_PERSISTENCE) { 350683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Slog.i(LOG_TAG, "[PERSIST START]"); 351683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 352683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 353683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav AtomicFile destination = new AtomicFile(mStatePersistFile); 354683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 355683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int version; 356683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final ArrayMap<String, Setting> settings; 357683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 358683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav synchronized (mLock) { 359683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav version = mVersion; 360683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav settings = new ArrayMap<>(mSettings); 361683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mDirty = false; 362683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mWriteScheduled = false; 363683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 364683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 365683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav FileOutputStream out = null; 366683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 367683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav out = destination.startWrite(); 368683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 369683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav XmlSerializer serializer = Xml.newSerializer(); 3709e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz serializer.setOutput(out, StandardCharsets.UTF_8.name()); 371c3f56c3cb51d486e581c26876e2ae1368f91e2caSvetoslav serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 372683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav serializer.startDocument(null, true); 373683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav serializer.startTag(null, TAG_SETTINGS); 374683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav serializer.attribute(null, ATTR_VERSION, String.valueOf(version)); 375683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 376683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int settingCount = settings.size(); 377683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav for (int i = 0; i < settingCount; i++) { 378683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Setting setting = settings.valueAt(i); 379683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 3803a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(), 3813a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki setting.getValue(), setting.getPackageName()); 382683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 383683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (DEBUG_PERSISTENCE) { 384683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" + setting.getValue()); 385683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 386683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 387683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 388683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav serializer.endTag(null, TAG_SETTINGS); 389683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav serializer.endDocument(); 390683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav destination.finishWrite(out); 391683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 392683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (DEBUG_PERSISTENCE) { 393683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Slog.i(LOG_TAG, "[PERSIST END]"); 394683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 395683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 396ba0821ed3bc2536be02df1ae850619b111cbd6f4Svet Ganov // Any error while writing is fatal. 397ba0821ed3bc2536be02df1ae850619b111cbd6f4Svet Ganov } catch (Throwable t) { 398ba0821ed3bc2536be02df1ae850619b111cbd6f4Svet Ganov Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t); 399683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav destination.failWrite(out); 400e723e54650c5ace8beb47bc4d3c493e276e65d91Svet Ganov throw new IllegalStateException("Failed to write settings, restoring backup", t); 401683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } finally { 402683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav IoUtils.closeQuietly(out); 403683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 404683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 405683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 4063a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki static void writeSingleSetting(int version, XmlSerializer serializer, String id, 4073a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki String name, String value, String packageName) throws IOException { 4083a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (id == null || isBinary(id) || name == null || isBinary(name) 4093a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki || packageName == null || isBinary(packageName)) { 4103a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // This shouldn't happen. 4113a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return; 4123a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4133a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.startTag(null, TAG_SETTING); 4143a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_ID, id); 4153a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_NAME, name); 4163a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki setValueAttribute(version, serializer, value); 4173a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_PACKAGE, packageName); 4183a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.endTag(null, TAG_SETTING); 4193a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4203a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 4213a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki static void setValueAttribute(int version, XmlSerializer serializer, String value) 4223a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki throws IOException { 4233a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (version >= SETTINGS_VERSOIN_NEW_ENCODING) { 4243a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (value == null) { 4253a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // Null value -> No ATTR_VALUE nor ATTR_VALUE_BASE64. 4263a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else if (isBinary(value)) { 4273a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_VALUE_BASE64, base64Encode(value)); 4283a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else { 4293a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_VALUE, value); 4303a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4313a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else { 4323a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // Old encoding. 4333a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (value == null) { 4343a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_VALUE, NULL_VALUE_OLD_STYLE); 4353a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else { 4363a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki serializer.attribute(null, ATTR_VALUE, value); 4373a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4383a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4393a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4403a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 4413a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private String getValueAttribute(XmlPullParser parser) { 4423a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (mVersion >= SETTINGS_VERSOIN_NEW_ENCODING) { 4433a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final String value = parser.getAttributeValue(null, ATTR_VALUE); 4443a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (value != null) { 4453a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return value; 4463a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4473a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final String base64 = parser.getAttributeValue(null, ATTR_VALUE_BASE64); 4483a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (base64 != null) { 4493a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return base64Decode(base64); 4503a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4513a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // null has neither ATTR_VALUE nor ATTR_VALUE_BASE64. 4523a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return null; 4533a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else { 4543a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // Old encoding. 4553a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final String stored = parser.getAttributeValue(null, ATTR_VALUE); 4563a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (NULL_VALUE_OLD_STYLE.equals(stored)) { 4573a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return null; 4583a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } else { 4593a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return stored; 4603a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4613a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4623a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 4633a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 464683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void readStateSyncLocked() { 465683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav FileInputStream in; 466683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (!mStatePersistFile.exists()) { 467683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 468683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 469683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 4703dcdd37b66bb996ae332c29e25788a118a9e2691Svetoslav in = new AtomicFile(mStatePersistFile).openRead(); 471683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } catch (FileNotFoundException fnfe) { 472683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Slog.i(LOG_TAG, "No settings state"); 473683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return; 474683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 475683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 476683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav XmlPullParser parser = Xml.newPullParser(); 4779e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz parser.setInput(in, StandardCharsets.UTF_8.name()); 478683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav parseStateLocked(parser); 479ba0821ed3bc2536be02df1ae850619b111cbd6f4Svet Ganov 480e723e54650c5ace8beb47bc4d3c493e276e65d91Svet Ganov } catch (XmlPullParserException | IOException e) { 481683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav throw new IllegalStateException("Failed parsing settings file: " 482e723e54650c5ace8beb47bc4d3c493e276e65d91Svet Ganov + mStatePersistFile , e); 483683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } finally { 484683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav IoUtils.closeQuietly(in); 485683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 486683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 487683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 488683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void parseStateLocked(XmlPullParser parser) 489683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav throws IOException, XmlPullParserException { 4908440ca3934dbb65743b357006e4a019e4351d479Svet Ganov final int outerDepth = parser.getDepth(); 4918440ca3934dbb65743b357006e4a019e4351d479Svet Ganov int type; 4928440ca3934dbb65743b357006e4a019e4351d479Svet Ganov while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 4938440ca3934dbb65743b357006e4a019e4351d479Svet Ganov && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 4948440ca3934dbb65743b357006e4a019e4351d479Svet Ganov if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 4958440ca3934dbb65743b357006e4a019e4351d479Svet Ganov continue; 4968440ca3934dbb65743b357006e4a019e4351d479Svet Ganov } 497683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 4988440ca3934dbb65743b357006e4a019e4351d479Svet Ganov String tagName = parser.getName(); 4998440ca3934dbb65743b357006e4a019e4351d479Svet Ganov if (tagName.equals(TAG_SETTINGS)) { 5008440ca3934dbb65743b357006e4a019e4351d479Svet Ganov parseSettingsLocked(parser); 5018440ca3934dbb65743b357006e4a019e4351d479Svet Ganov } 502683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 503683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 504683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 5058440ca3934dbb65743b357006e4a019e4351d479Svet Ganov private void parseSettingsLocked(XmlPullParser parser) 506683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav throws IOException, XmlPullParserException { 507c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov 508c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); 509c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov 5108440ca3934dbb65743b357006e4a019e4351d479Svet Ganov final int outerDepth = parser.getDepth(); 5118440ca3934dbb65743b357006e4a019e4351d479Svet Ganov int type; 5128440ca3934dbb65743b357006e4a019e4351d479Svet Ganov while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 5138440ca3934dbb65743b357006e4a019e4351d479Svet Ganov && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 5148440ca3934dbb65743b357006e4a019e4351d479Svet Ganov if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 5158440ca3934dbb65743b357006e4a019e4351d479Svet Ganov continue; 5168440ca3934dbb65743b357006e4a019e4351d479Svet Ganov } 517683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 518c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov String tagName = parser.getName(); 519c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov if (tagName.equals(TAG_SETTING)) { 520c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov String id = parser.getAttributeValue(null, ATTR_ID); 521c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov String name = parser.getAttributeValue(null, ATTR_NAME); 5223a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki String value = getValueAttribute(parser); 523c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 5243a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki mSettings.put(name, new Setting(name, value, packageName, id)); 525683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 526c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov if (DEBUG_PERSISTENCE) { 527c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value); 528c9755bc4f2183d6d8e035e6a448b2c948dcd3a01Svet Ganov } 529683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 530683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 531683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 532683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 533683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final class MyHandler extends Handler { 534683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public static final int MSG_PERSIST_SETTINGS = 1; 535683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 536683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public MyHandler() { 537e1519582ab3010a132d4c4f1fa5cc92fad7cbc39Svetoslav Ganov super(BackgroundThread.getHandler().getLooper()); 538683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 539683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 540683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav @Override 541683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public void handleMessage(Message message) { 542683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav switch (message.what) { 543683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav case MSG_PERSIST_SETTINGS: { 544683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Runnable callback = (Runnable) message.obj; 545683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav doWriteState(); 546683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (callback != null) { 547683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav callback.run(); 548683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 549683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 550683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav break; 551683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 552683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 553683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 554683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 555b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav public final class Setting { 556683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private String name; 557683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private String value; 558683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private String packageName; 559683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private String id; 560683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 561683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public Setting(String name, String value, String packageName) { 562b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav init(name, value, packageName, String.valueOf(mNextId++)); 563683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 564683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 565683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public Setting(String name, String value, String packageName, String id) { 566b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav mNextId = Math.max(mNextId, Long.valueOf(id) + 1); 567b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav init(name, value, packageName, id); 568683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 569683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 570683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void init(String name, String value, String packageName, String id) { 571683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.name = name; 572683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.value = value; 573683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.packageName = packageName; 574683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.id = id; 575683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 576683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 577683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public String getName() { 578683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return name; 579683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 580683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 581683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public String getValue() { 582683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return value; 583683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 584683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 585683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public String getPackageName() { 586683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return packageName; 587683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 588683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 589683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public String getId() { 590683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return id; 591683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 592683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 593683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public boolean update(String value, String packageName) { 594683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (Objects.equal(value, this.value)) { 595683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return false; 596683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 597683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.value = value; 598683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.packageName = packageName; 599b505ccc90667ad69a1b122f025a415a3b2aee6afSvetoslav this.id = String.valueOf(mNextId++); 600683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return true; 601683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 602683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 6033a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6043a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki /** 6053a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki * @return TRUE if a string is considered "binary" from KXML's point of view. NOTE DO NOT 6063a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki * pass null. 6073a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki */ 6083a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki public static boolean isBinary(String s) { 6093a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (s == null) { 6103a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki throw new NullPointerException(); 6113a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6123a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // See KXmlSerializer.writeEscaped 6133a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki for (int i = 0; i < s.length(); i++) { 6143a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki char c = s.charAt(i); 6153a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd); 6163a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki if (!allowedInXml) { 6173a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return true; 6183a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6193a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6203a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return false; 6213a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6223a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6233a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static String base64Encode(String s) { 6243a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return Base64.encodeToString(toBytes(s), Base64.NO_WRAP); 6253a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6263a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6273a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static String base64Decode(String s) { 6283a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return fromBytes(Base64.decode(s, Base64.DEFAULT)); 6293a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6303a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6313a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // Note the followings are basically just UTF-16 encode/decode. But we want to preserve 6323a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // contents as-is, even if it contains broken surrogate pairs, we do it by ourselves, 6333a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki // since I don't know how Charset would treat them. 6343a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6353a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static byte[] toBytes(String s) { 6363a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final byte[] result = new byte[s.length() * 2]; 6373a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki int resultIndex = 0; 6383a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki for (int i = 0; i < s.length(); ++i) { 6393a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki char ch = s.charAt(i); 6403a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki result[resultIndex++] = (byte) (ch >> 8); 6413a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki result[resultIndex++] = (byte) ch; 6423a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6433a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return result; 6443a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6453a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6463a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki private static String fromBytes(byte[] bytes) { 6473a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final StringBuffer sb = new StringBuffer(bytes.length / 2); 6483a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6493a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final int last = bytes.length - 1; 6503a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki 6513a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki for (int i = 0; i < last; i += 2) { 6523a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki final char ch = (char) ((bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff)); 6533a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki sb.append(ch); 6543a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 6553a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki return sb.toString(); 6563a2c3578ba5bf8642c994fa357a96eaa4a38cdc9Makoto Onuki } 657683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav} 658