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